@omgbase/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/cli/eval-matcher.d.ts +13 -0
  3. package/dist/src/cli/eval-matcher.js +36 -0
  4. package/dist/src/core/attach.d.ts +5 -0
  5. package/dist/src/core/attach.js +38 -0
  6. package/dist/src/core/cursor.d.ts +11 -0
  7. package/dist/src/core/cursor.js +35 -0
  8. package/dist/src/core/hash.d.ts +23 -0
  9. package/dist/src/core/hash.js +82 -0
  10. package/dist/src/core/ids.d.ts +6 -0
  11. package/dist/src/core/ids.js +26 -0
  12. package/dist/src/core/index.d.ts +1 -0
  13. package/dist/src/core/index.js +2 -0
  14. package/dist/src/core/ingest.d.ts +43 -0
  15. package/dist/src/core/ingest.js +287 -0
  16. package/dist/src/core/order-key.d.ts +8 -0
  17. package/dist/src/core/order-key.js +72 -0
  18. package/dist/src/core/parse/parse.d.ts +3 -0
  19. package/dist/src/core/parse/parse.js +104 -0
  20. package/dist/src/core/parse/render.d.ts +3 -0
  21. package/dist/src/core/parse/render.js +20 -0
  22. package/dist/src/core/parse/tree.d.ts +8 -0
  23. package/dist/src/core/parse/tree.js +43 -0
  24. package/dist/src/core/parse/types.d.ts +52 -0
  25. package/dist/src/core/parse/types.js +9 -0
  26. package/dist/src/core/read/document.d.ts +107 -0
  27. package/dist/src/core/read/document.js +191 -0
  28. package/dist/src/core/read/nodes.d.ts +47 -0
  29. package/dist/src/core/read/nodes.js +102 -0
  30. package/dist/src/core/read/outline.d.ts +12 -0
  31. package/dist/src/core/read/outline.js +76 -0
  32. package/dist/src/core/read/reader.d.ts +125 -0
  33. package/dist/src/core/read/reader.js +206 -0
  34. package/dist/src/core/read/refs.d.ts +15 -0
  35. package/dist/src/core/read/refs.js +51 -0
  36. package/dist/src/core/store/edges.d.ts +26 -0
  37. package/dist/src/core/store/edges.js +101 -0
  38. package/dist/src/core/store/fts.d.ts +10 -0
  39. package/dist/src/core/store/fts.js +22 -0
  40. package/dist/src/core/store/gc.d.ts +18 -0
  41. package/dist/src/core/store/gc.js +69 -0
  42. package/dist/src/core/store/nodes.d.ts +5 -0
  43. package/dist/src/core/store/nodes.js +57 -0
  44. package/dist/src/core/store/properties.d.ts +38 -0
  45. package/dist/src/core/store/properties.js +177 -0
  46. package/dist/src/core/store/rebuild.d.ts +3 -0
  47. package/dist/src/core/store/rebuild.js +29 -0
  48. package/dist/src/core/store/schema.d.ts +9 -0
  49. package/dist/src/core/store/schema.js +386 -0
  50. package/dist/src/core/store/sections.d.ts +2 -0
  51. package/dist/src/core/store/sections.js +29 -0
  52. package/dist/src/core/store/store.d.ts +24 -0
  53. package/dist/src/core/store/store.js +164 -0
  54. package/dist/src/core/store/writers.d.ts +52 -0
  55. package/dist/src/core/store/writers.js +89 -0
  56. package/dist/src/core/vec.d.ts +10 -0
  57. package/dist/src/core/vec.js +33 -0
  58. package/dist/src/format/adapter.d.ts +97 -0
  59. package/dist/src/format/adapter.js +14 -0
  60. package/dist/src/format/index.d.ts +6 -0
  61. package/dist/src/format/index.js +15 -0
  62. package/dist/src/format/json.d.ts +3 -0
  63. package/dist/src/format/json.js +324 -0
  64. package/dist/src/format/markdown.d.ts +5 -0
  65. package/dist/src/format/markdown.js +217 -0
  66. package/dist/src/format/registry.d.ts +6 -0
  67. package/dist/src/format/registry.js +24 -0
  68. package/dist/src/format/yaml.d.ts +5 -0
  69. package/dist/src/format/yaml.js +413 -0
  70. package/dist/src/graph/extract.d.ts +38 -0
  71. package/dist/src/graph/extract.js +242 -0
  72. package/dist/src/graph/history.d.ts +103 -0
  73. package/dist/src/graph/history.js +210 -0
  74. package/dist/src/graph/inbound-links.d.ts +33 -0
  75. package/dist/src/graph/inbound-links.js +130 -0
  76. package/dist/src/graph/link-destinations.d.ts +22 -0
  77. package/dist/src/graph/link-destinations.js +84 -0
  78. package/dist/src/graph/link-health.d.ts +79 -0
  79. package/dist/src/graph/link-health.js +146 -0
  80. package/dist/src/graph/links.d.ts +23 -0
  81. package/dist/src/graph/links.js +42 -0
  82. package/dist/src/index.d.ts +60 -0
  83. package/dist/src/index.js +70 -0
  84. package/dist/src/mcp/errors.d.ts +17 -0
  85. package/dist/src/mcp/errors.js +18 -0
  86. package/dist/src/mcp/graph.d.ts +63 -0
  87. package/dist/src/mcp/graph.js +212 -0
  88. package/dist/src/mcp/reference.d.ts +1 -0
  89. package/dist/src/mcp/reference.js +227 -0
  90. package/dist/src/mcp/server.d.ts +30 -0
  91. package/dist/src/mcp/server.js +1076 -0
  92. package/dist/src/mcp/stdio.d.ts +13 -0
  93. package/dist/src/mcp/stdio.js +24 -0
  94. package/dist/src/migrate/mrplex.d.ts +27 -0
  95. package/dist/src/migrate/mrplex.js +29 -0
  96. package/dist/src/mutate/apply.d.ts +83 -0
  97. package/dist/src/mutate/apply.js +302 -0
  98. package/dist/src/mutate/doc-store.d.ts +49 -0
  99. package/dist/src/mutate/doc-store.js +71 -0
  100. package/dist/src/mutate/docs.d.ts +60 -0
  101. package/dist/src/mutate/docs.js +232 -0
  102. package/dist/src/mutate/known-ids.d.ts +12 -0
  103. package/dist/src/mutate/known-ids.js +154 -0
  104. package/dist/src/mutate/load.d.ts +3 -0
  105. package/dist/src/mutate/load.js +66 -0
  106. package/dist/src/mutate/lower.d.ts +32 -0
  107. package/dist/src/mutate/lower.js +232 -0
  108. package/dist/src/mutate/macros.d.ts +91 -0
  109. package/dist/src/mutate/macros.js +230 -0
  110. package/dist/src/mutate/ops.d.ts +40 -0
  111. package/dist/src/mutate/ops.js +513 -0
  112. package/dist/src/mutate/opset.d.ts +92 -0
  113. package/dist/src/mutate/opset.js +79 -0
  114. package/dist/src/mutate/plan-update.d.ts +51 -0
  115. package/dist/src/mutate/plan-update.js +187 -0
  116. package/dist/src/mutate/tree.d.ts +46 -0
  117. package/dist/src/mutate/tree.js +167 -0
  118. package/dist/src/oqx/run.d.ts +1 -0
  119. package/dist/src/oqx/run.js +6 -0
  120. package/dist/src/oqx-js/context.d.ts +23 -0
  121. package/dist/src/oqx-js/context.js +564 -0
  122. package/dist/src/oqx-js/planner.d.ts +10 -0
  123. package/dist/src/oqx-js/planner.js +91 -0
  124. package/dist/src/oqx-js/run.d.ts +41 -0
  125. package/dist/src/oqx-js/run.js +332 -0
  126. package/dist/src/oqx-js/sql/translate.d.ts +27 -0
  127. package/dist/src/oqx-js/sql/translate.js +290 -0
  128. package/dist/src/reconcile/crossdoc.d.ts +34 -0
  129. package/dist/src/reconcile/crossdoc.js +76 -0
  130. package/dist/src/reconcile/eval/generator.d.ts +21 -0
  131. package/dist/src/reconcile/eval/generator.js +129 -0
  132. package/dist/src/reconcile/eval/metrics.d.ts +32 -0
  133. package/dist/src/reconcile/eval/metrics.js +74 -0
  134. package/dist/src/reconcile/flatten.d.ts +12 -0
  135. package/dist/src/reconcile/flatten.js +37 -0
  136. package/dist/src/reconcile/phase5.d.ts +8 -0
  137. package/dist/src/reconcile/phase5.js +167 -0
  138. package/dist/src/reconcile/phase6a.d.ts +2 -0
  139. package/dist/src/reconcile/phase6a.js +159 -0
  140. package/dist/src/reconcile/phases.d.ts +28 -0
  141. package/dist/src/reconcile/phases.js +0 -0
  142. package/dist/src/reconcile/reconcile.d.ts +21 -0
  143. package/dist/src/reconcile/reconcile.js +115 -0
  144. package/dist/src/reconcile/similarity.d.ts +7 -0
  145. package/dist/src/reconcile/similarity.js +41 -0
  146. package/dist/src/reconcile/types.d.ts +46 -0
  147. package/dist/src/reconcile/types.js +19 -0
  148. package/dist/src/search/cel/ast.d.ts +74 -0
  149. package/dist/src/search/cel/ast.js +3 -0
  150. package/dist/src/search/cel/compile.d.ts +60 -0
  151. package/dist/src/search/cel/compile.js +836 -0
  152. package/dist/src/search/cel/lexer.d.ts +9 -0
  153. package/dist/src/search/cel/lexer.js +139 -0
  154. package/dist/src/search/cel/parser.d.ts +7 -0
  155. package/dist/src/search/cel/parser.js +219 -0
  156. package/dist/src/search/drain.d.ts +39 -0
  157. package/dist/src/search/drain.js +97 -0
  158. package/dist/src/search/embeddings.d.ts +131 -0
  159. package/dist/src/search/embeddings.js +263 -0
  160. package/dist/src/search/external.d.ts +13 -0
  161. package/dist/src/search/external.js +177 -0
  162. package/dist/src/search/fts-query.d.ts +1 -0
  163. package/dist/src/search/fts-query.js +64 -0
  164. package/dist/src/search/provider.d.ts +17 -0
  165. package/dist/src/search/provider.js +13 -0
  166. package/dist/src/search/resolve.d.ts +18 -0
  167. package/dist/src/search/resolve.js +23 -0
  168. package/dist/src/search/rrf.d.ts +36 -0
  169. package/dist/src/search/rrf.js +84 -0
  170. package/dist/src/search/tasks.d.ts +12 -0
  171. package/dist/src/search/tasks.js +131 -0
  172. package/dist/src/search/text.d.ts +16 -0
  173. package/dist/src/search/text.js +21 -0
  174. package/dist/src/search/vector.d.ts +18 -0
  175. package/dist/src/search/vector.js +49 -0
  176. package/dist/src/sync/admin.d.ts +59 -0
  177. package/dist/src/sync/admin.js +53 -0
  178. package/dist/src/sync/attach.d.ts +13 -0
  179. package/dist/src/sync/attach.js +29 -0
  180. package/dist/src/sync/checkpoint.d.ts +21 -0
  181. package/dist/src/sync/checkpoint.js +52 -0
  182. package/dist/src/sync/driver.d.ts +25 -0
  183. package/dist/src/sync/driver.js +80 -0
  184. package/dist/src/sync/external-source.d.ts +14 -0
  185. package/dist/src/sync/external-source.js +137 -0
  186. package/dist/src/sync/freshness.d.ts +42 -0
  187. package/dist/src/sync/freshness.js +152 -0
  188. package/dist/src/sync/fs-util.d.ts +12 -0
  189. package/dist/src/sync/fs-util.js +55 -0
  190. package/dist/src/sync/git-heuristics.d.ts +6 -0
  191. package/dist/src/sync/git-heuristics.js +16 -0
  192. package/dist/src/sync/observe.d.ts +63 -0
  193. package/dist/src/sync/observe.js +84 -0
  194. package/dist/src/sync/plugin.d.ts +54 -0
  195. package/dist/src/sync/plugin.js +12 -0
  196. package/dist/src/sync/reconciling-ingest.d.ts +12 -0
  197. package/dist/src/sync/reconciling-ingest.js +209 -0
  198. package/dist/src/sync/recovery.d.ts +6 -0
  199. package/dist/src/sync/recovery.js +34 -0
  200. package/dist/src/sync/settings.d.ts +18 -0
  201. package/dist/src/sync/settings.js +58 -0
  202. package/dist/src/sync/sources.d.ts +37 -0
  203. package/dist/src/sync/sources.js +75 -0
  204. package/dist/src/sync/tombstone.d.ts +9 -0
  205. package/dist/src/sync/tombstone.js +55 -0
  206. package/dist/src/sync/watch-lease.d.ts +14 -0
  207. package/dist/src/sync/watch-lease.js +91 -0
  208. package/dist/src/sync/watcher.d.ts +19 -0
  209. package/dist/src/sync/watcher.js +34 -0
  210. package/dist/src/sync/workspace.d.ts +34 -0
  211. package/dist/src/sync/workspace.js +120 -0
  212. package/dist/src/sync/writer-lock.d.ts +16 -0
  213. package/dist/src/sync/writer-lock.js +121 -0
  214. package/package.json +80 -0
@@ -0,0 +1,79 @@
1
+ /** The bare kernel ops, in order — what `apply` actually replays. */
2
+ export function opsetKernelOps(opset) {
3
+ return opset.ops.map((p) => p.op);
4
+ }
5
+ /** Compute the summary from the planned ops + carried/ambiguous counts. */
6
+ export function summarizeOps(ops, counts) {
7
+ const s = {
8
+ preserved: counts.preserved,
9
+ updated: 0, moved: 0, created: 0, removed: 0, split: 0, merged: 0,
10
+ ambiguous: counts.ambiguous,
11
+ };
12
+ for (const p of ops) {
13
+ switch (p.disposition) {
14
+ case "edited":
15
+ s.updated++;
16
+ break;
17
+ case "moved":
18
+ s.moved++;
19
+ break;
20
+ case "edited_moved":
21
+ s.updated++;
22
+ s.moved++;
23
+ break;
24
+ case "inserted":
25
+ case "copied_from":
26
+ case "resurrected":
27
+ s.created++;
28
+ break;
29
+ case "deleted":
30
+ s.removed++;
31
+ break;
32
+ case "split_from":
33
+ s.split++;
34
+ break;
35
+ case "merged_into":
36
+ s.merged++;
37
+ break;
38
+ // "retiled"/"same"/"bulk_rewrite" carry no summary bucket of their own.
39
+ }
40
+ }
41
+ return s;
42
+ }
43
+ /** Serialize an opset to a stable JSON string (ops are plain JSON). */
44
+ export function serializeOpset(opset) {
45
+ return JSON.stringify(opset);
46
+ }
47
+ /** Parse a serialized opset, validating the version tag. */
48
+ export function parseOpset(text) {
49
+ const o = JSON.parse(text);
50
+ if (o.version !== 1 || o.kind !== "doc_update") {
51
+ throw new Error(`unrecognized opset (version ${String(o.version)})`);
52
+ }
53
+ return o;
54
+ }
55
+ // A short human-/agent-readable rendering that makes identity consequences
56
+ // obvious (04-mutation §, the note's example). One line per op + a summary.
57
+ const VERB = {
58
+ same: "KEEP ", edited: "UPDATE", moved: "MOVE ", edited_moved: "UPDATE",
59
+ inserted: "INSERT", deleted: "REMOVE", split_from: "SPLIT ", merged_into: "MERGE ",
60
+ copied_from: "COPY ", resurrected: "RESURR", bulk_rewrite: "REWRITE", retiled: "RETILE",
61
+ };
62
+ export function renderOpsetPlan(opset) {
63
+ const lines = [];
64
+ for (const p of opset.ops) {
65
+ const subject = p.blocks[0] ?? "(new)";
66
+ const conf = p.confidence !== null ? ` ~${p.confidence.toFixed(2)}` : "";
67
+ const why = p.reason ? ` [${p.reason}]` : "";
68
+ lines.push(`${VERB[p.disposition]} ${subject.padEnd(9)} ${p.disposition}${conf}${why}`);
69
+ }
70
+ const s = opset.summary;
71
+ lines.push("");
72
+ lines.push(`preserved: ${s.preserved} updated: ${s.updated} moved: ${s.moved} ` +
73
+ `created: ${s.created} removed: ${s.removed} split: ${s.split} ` +
74
+ `merged: ${s.merged} ambiguous: ${s.ambiguous}`);
75
+ if (!opset.converges)
76
+ lines.push(`WARNING: plan does not reproduce the proposed content exactly — will not apply.`);
77
+ return lines.join("\n");
78
+ }
79
+ //# sourceMappingURL=opset.js.map
@@ -0,0 +1,51 @@
1
+ import type { Store } from "../core/store/store.js";
2
+ import { type ReconcileConfig } from "../reconcile/types.js";
3
+ import { type ApplyResult } from "./apply.js";
4
+ import { type Opset } from "./opset.js";
5
+ export interface PlanUpdateOptions {
6
+ config?: ReconcileConfig;
7
+ }
8
+ /**
9
+ * Plan a whole-document update. `docRef` is a doc id or repo-relative path;
10
+ * `content` is the proposed complete document. Throws doc_missing if the
11
+ * document does not exist (a create is docsCreate's job). Never writes.
12
+ */
13
+ export declare function planUpdate(store: Store, repoId: string, rootPath: string, docRef: string, content: string, opts?: PlanUpdateOptions): Opset;
14
+ export interface ApplyOpsetRequest {
15
+ repoId: string;
16
+ rootPath: string;
17
+ opset: Opset;
18
+ origin: {
19
+ actor: string;
20
+ reason?: string;
21
+ };
22
+ dryRun?: boolean;
23
+ omgbaseDir?: string;
24
+ }
25
+ /**
26
+ * Apply an opset through the kernel write path. Validates the plan's
27
+ * preconditions against current state first (a stale plan fails, carrying
28
+ * current truth, rather than silently reinterpreting) and refuses a plan that
29
+ * did not verify byte-convergent.
30
+ */
31
+ export declare function applyOpset(store: Store, req: ApplyOpsetRequest): ApplyResult;
32
+ export interface DocsUpdateContext {
33
+ repoId: string;
34
+ rootPath: string;
35
+ omgbaseDir?: string;
36
+ actor?: string;
37
+ }
38
+ export interface DocsUpdateResult {
39
+ opset: Opset;
40
+ /** null on dry_run (plan only) */
41
+ result: ApplyResult | null;
42
+ }
43
+ /**
44
+ * Whole-document update convenience: plan then apply. `dryRun` returns the
45
+ * opset without executing (the note's docs.update(..., dry_run=true)).
46
+ */
47
+ export declare function docsUpdate(store: Store, ctx: DocsUpdateContext, docRef: string, content: string, opts?: {
48
+ dryRun?: boolean;
49
+ config?: ReconcileConfig;
50
+ reason?: string;
51
+ }): DocsUpdateResult;
@@ -0,0 +1,187 @@
1
+ import { sha256 } from "../core/hash.js";
2
+ import { parseTree } from "../core/parse/tree.js";
3
+ import { adapterForPath } from "../format/index.js";
4
+ import { findDocByRef } from "../core/read/reader.js";
5
+ import { reconcileDocument } from "../reconcile/reconcile.js";
6
+ import { flatten } from "../reconcile/flatten.js";
7
+ import { DEFAULT_CONFIG } from "../reconcile/types.js";
8
+ import { loadOldMatchBlocks } from "../sync/reconciling-ingest.js";
9
+ import { loadMutDoc } from "./load.js";
10
+ import { renderDoc, MutationError } from "./tree.js";
11
+ import { apply } from "./apply.js";
12
+ import { lowerTopLevel, lowerReplace } from "./lower.js";
13
+ import { summarizeOps, opsetKernelOps } from "./opset.js";
14
+ function toFlatSource(b) {
15
+ return { type: b.type, raw: b.raw, children: b.children.map(toFlatSource) };
16
+ }
17
+ function extractFrontmatter(blocks) {
18
+ if (blocks.length > 0 && blocks[0].type === "frontmatter")
19
+ return { fmBlock: blocks[0], rest: blocks.slice(1) };
20
+ return { fmBlock: null, rest: blocks };
21
+ }
22
+ /**
23
+ * Plan a whole-document update. `docRef` is a doc id or repo-relative path;
24
+ * `content` is the proposed complete document. Throws doc_missing if the
25
+ * document does not exist (a create is docsCreate's job). Never writes.
26
+ */
27
+ export function planUpdate(store, repoId, rootPath, docRef, content, opts = {}) {
28
+ const config = opts.config ?? DEFAULT_CONFIG;
29
+ const info = findDocByRef(store, repoId, docRef);
30
+ if (!info)
31
+ throw new MutationError("doc_missing", `doc ${docRef} not found`, { doc: docRef });
32
+ const docId = info.docId;
33
+ const path = info.path;
34
+ const oldDoc = loadMutDoc(store.db, docId);
35
+ if (!oldDoc)
36
+ throw new MutationError("doc_missing", `doc ${docRef} not found`, { doc: docRef });
37
+ // Base state the plan is pinned to. baseContentHash is the current rendered
38
+ // bytes (== docs.file_hash at quiescence); a stale plan is rejected at apply.
39
+ const currentContent = renderDoc(oldDoc);
40
+ const baseContentHash = sha256(currentContent).toString("hex");
41
+ // Parse the proposed content with the doc's format adapter.
42
+ const adapter = adapterForPath(path);
43
+ const tree = adapter ? adapter.parse(content) : parseTree(content);
44
+ const { fmBlock, rest } = extractFrontmatter(tree.children);
45
+ // Reconcile the proposed body against the current tree (no resurrection pool:
46
+ // the op path mints new ids for new structure rather than reusing tombstones).
47
+ const oldMatch = loadOldMatchBlocks(store.db, docId);
48
+ const newMatch = flatten(rest.map(toFlatSource));
49
+ const result = reconcileDocument(oldMatch, newMatch, { config });
50
+ // Frontmatter is a document-level unit outside the six block ops. loadMutDoc
51
+ // builds frontmatterRaw as `bytes + separator`; mirror that for the target.
52
+ const targetFm = fmBlock ? fmBlock.raw + fmBlock.trivia : null;
53
+ const fmChanged = targetFm !== oldDoc.frontmatterRaw;
54
+ const setFrontmatter = fmChanged ? [{ doc: docId, raw: targetFm }] : undefined;
55
+ const diagnostics = [];
56
+ // Try lowerings in order of decreasing identity preservation; take the first
57
+ // that reproduces the proposed content exactly.
58
+ let chosen = null;
59
+ let converges = false;
60
+ const t2 = lowerTopLevel(oldDoc, rest, assignmentOf(result), dispositionsOf(result));
61
+ const v2 = verify(store, repoId, rootPath, path, opsOf(t2), setFrontmatter, content, currentContent);
62
+ if (v2.ok) {
63
+ chosen = t2;
64
+ converges = true;
65
+ }
66
+ else {
67
+ diagnostics.push(`top-level lowering did not reproduce the proposed content byte-for-byte (${describeDivergence(v2, tree.children, content)}); falling back to full replace`);
68
+ const t3 = lowerReplace(oldDoc, content);
69
+ const v3 = verify(store, repoId, rootPath, path, opsOf(t3), setFrontmatter, content, currentContent);
70
+ if (v3.ok) {
71
+ chosen = t3;
72
+ converges = true;
73
+ diagnostics.push("full-replace plan converges (block identity not preserved)");
74
+ }
75
+ else {
76
+ // Neither converged: return the identity-preserving plan but flag it. apply
77
+ // refuses a non-convergent plan (loud failure), so no wrong bytes land.
78
+ chosen = t2;
79
+ diagnostics.push(`no lowering reproduced the proposed content exactly (full replace: ${describeDivergence(v3, tree.children, content)}); plan will not apply — inspect and re-plan`);
80
+ }
81
+ }
82
+ const ops = chosen.ops;
83
+ const summary = summarizeOps(ops, { preserved: chosen.preserved, ambiguous: chosen.ambiguous });
84
+ return {
85
+ version: 1,
86
+ kind: "doc_update",
87
+ target: { doc: docId, path },
88
+ precondition: { doc: docId, path, baseRevision: info.currentRev, baseContentHash },
89
+ matcherV: config.matcherV,
90
+ ops,
91
+ ...(fmChanged ? { frontmatter: { raw: targetFm } } : {}),
92
+ summary,
93
+ converges,
94
+ diagnostics,
95
+ };
96
+ }
97
+ function assignmentOf(r) { return r.assignment; }
98
+ function dispositionsOf(r) {
99
+ return r.dispositions;
100
+ }
101
+ function opsOf(l) { return l.ops.map((p) => p.op); }
102
+ // Simulate a candidate op list and check the rendered result equals the exact
103
+ // proposed content. An invalid script throws in apply → treated as non-convergent.
104
+ function verify(store, repoId, rootPath, path, ops, setFrontmatter, expected, currentContent) {
105
+ try {
106
+ const res = apply(store, { repoId, rootPath, ops, origin: { actor: "plan:verify" }, dryRun: true, ...(setFrontmatter ? { setFrontmatter } : {}) });
107
+ // A doc untouched by any op (e.g. an empty plan) is absent from diffs; its
108
+ // effective result is the unchanged current content.
109
+ const after = res.diffs?.[path]?.after ?? currentContent;
110
+ return after === expected ? { ok: true } : { ok: false, actual: after, error: null };
111
+ }
112
+ catch (e) {
113
+ return { ok: false, actual: null, error: e instanceof Error ? `${e.code ?? "error"}: ${e.message}` : String(e) };
114
+ }
115
+ }
116
+ // Name the failure precisely: the first differing byte offset and the proposed
117
+ // top-level block (index + type) whose span covers it, so a caller learns WHICH
118
+ // construct failed to round-trip rather than just "no lowering converged".
119
+ function describeDivergence(v, proposed, expected) {
120
+ if (v.ok)
121
+ return "converged";
122
+ if (v.actual === null)
123
+ return `simulation threw ${v.error}`;
124
+ const actual = v.actual;
125
+ let off = 0;
126
+ const n = Math.min(actual.length, expected.length);
127
+ while (off < n && actual.charCodeAt(off) === expected.charCodeAt(off))
128
+ off++;
129
+ const idx = proposed.findIndex((b) => off >= b.span.start && off < b.span.end + b.trivia.length);
130
+ const where = idx >= 0 ? `proposed block #${idx} (${proposed[idx].type}, bytes ${proposed[idx].span.start}-${proposed[idx].span.end})` : off >= expected.length ? "past the end of the proposed content" : "leading trivia";
131
+ const snippet = (s) => JSON.stringify(s.slice(off, off + 24));
132
+ return `first divergence at byte ${off} in ${where}: expected ${snippet(expected)}, rendered ${snippet(actual)}`;
133
+ }
134
+ /**
135
+ * Apply an opset through the kernel write path. Validates the plan's
136
+ * preconditions against current state first (a stale plan fails, carrying
137
+ * current truth, rather than silently reinterpreting) and refuses a plan that
138
+ * did not verify byte-convergent.
139
+ */
140
+ export function applyOpset(store, req) {
141
+ const { opset } = req;
142
+ if (!opset.converges) {
143
+ const why = opset.diagnostics.length > 0 ? ` — ${opset.diagnostics[opset.diagnostics.length - 1]}` : "";
144
+ throw new MutationError("plan_not_convergent", `opset does not reproduce the proposed content; re-plan${why}`, { diagnostics: opset.diagnostics });
145
+ }
146
+ // Precondition: the document must still be at the revision/content the plan
147
+ // was computed against.
148
+ const row = store.db.prepare("SELECT current_rev, lower(hex(file_hash)) fh FROM docs WHERE doc_id = ? AND deleted_commit IS NULL").get(opset.precondition.doc);
149
+ if (!row)
150
+ throw new MutationError("doc_missing", `doc ${opset.precondition.doc} not found`, { doc: opset.precondition.doc });
151
+ const stale = (opset.precondition.baseRevision !== null && row.current_rev !== opset.precondition.baseRevision) || (row.fh !== null && row.fh !== opset.precondition.baseContentHash);
152
+ if (stale) {
153
+ throw new MutationError("stale_plan", "document changed since the plan was computed; re-plan", {
154
+ doc: opset.precondition.doc,
155
+ expected_revision: opset.precondition.baseRevision,
156
+ current: { revision: row.current_rev, content_hash: row.fh },
157
+ retriable: true,
158
+ });
159
+ }
160
+ return apply(store, {
161
+ repoId: req.repoId,
162
+ rootPath: req.rootPath,
163
+ ops: opsetKernelOps(opset),
164
+ origin: req.origin,
165
+ ...(req.dryRun !== undefined ? { dryRun: req.dryRun } : {}),
166
+ ...(req.omgbaseDir !== undefined ? { omgbaseDir: req.omgbaseDir } : {}),
167
+ ...(opset.frontmatter ? { setFrontmatter: [{ doc: opset.precondition.doc, raw: opset.frontmatter.raw }] } : {}),
168
+ });
169
+ }
170
+ /**
171
+ * Whole-document update convenience: plan then apply. `dryRun` returns the
172
+ * opset without executing (the note's docs.update(..., dry_run=true)).
173
+ */
174
+ export function docsUpdate(store, ctx, docRef, content, opts = {}) {
175
+ const opset = planUpdate(store, ctx.repoId, ctx.rootPath, docRef, content, opts.config ? { config: opts.config } : {});
176
+ if (opts.dryRun)
177
+ return { opset, result: null };
178
+ const result = applyOpset(store, {
179
+ repoId: ctx.repoId,
180
+ rootPath: ctx.rootPath,
181
+ opset,
182
+ origin: { actor: ctx.actor ?? "agent:update", ...(opts.reason !== undefined ? { reason: opts.reason } : {}) },
183
+ ...(ctx.omgbaseDir !== undefined ? { omgbaseDir: ctx.omgbaseDir } : {}),
184
+ });
185
+ return { opset, result };
186
+ }
187
+ //# sourceMappingURL=plan-update.js.map
@@ -0,0 +1,46 @@
1
+ export interface MutBlock {
2
+ id: string;
3
+ type: string;
4
+ raw: string;
5
+ trivia: string;
6
+ attrs: Record<string, unknown>;
7
+ children: MutBlock[];
8
+ /** true if raw was supplied by an op (serialize as-is; 03 §2.2) */
9
+ dirty?: boolean;
10
+ }
11
+ export interface MutDoc {
12
+ docId: string;
13
+ path: string;
14
+ format: string;
15
+ leadingTrivia: string;
16
+ frontmatterRaw: string | null;
17
+ children: MutBlock[];
18
+ }
19
+ export declare class MutationError extends Error {
20
+ code: string;
21
+ data: Record<string, unknown>;
22
+ constructor(code: string, message: string, data?: Record<string, unknown>);
23
+ }
24
+ /** Find a block by id anywhere in the doc; returns the block + its parent list. */
25
+ export declare function locate(doc: MutDoc, blockId: string): {
26
+ block: MutBlock;
27
+ siblings: MutBlock[];
28
+ index: number;
29
+ } | null;
30
+ export declare function rawHashHex(raw: string): string;
31
+ /** The block that owns `children` (by reference identity), or null at top level. */
32
+ export declare function ownerOf(doc: MutDoc, children: MutBlock[]): MutBlock | null;
33
+ /**
34
+ * A container's raw is the authoritative render source (03 §2.2), so a
35
+ * structural change to its children (insert/remove/move) must invalidate the
36
+ * container's cached raw. Mark the owning container dirty so renderBlock
37
+ * reconstructs it from the mutated children rather than emitting the now-stale
38
+ * raw verbatim; ancestors see it via hasDirtyDescendant and reconstruct in turn.
39
+ * Top-level siblings (doc.children) need no marking — renderDoc renders each
40
+ * top-level block directly.
41
+ */
42
+ export declare function markContainerDirty(doc: MutDoc, children: MutBlock[]): void;
43
+ export declare function renderDoc(doc: MutDoc): string;
44
+ /** Ordered ids of a parent's direct children (for parent_children_hash CAS). */
45
+ export declare function childIds(list: MutBlock[]): string[];
46
+ export declare function parentChildrenHash(list: MutBlock[]): string;
@@ -0,0 +1,167 @@
1
+ import { sha256 } from "../core/hash.js";
2
+ export class MutationError extends Error {
3
+ code;
4
+ data;
5
+ constructor(code, message, data = {}) {
6
+ super(message);
7
+ this.code = code;
8
+ this.data = data;
9
+ }
10
+ }
11
+ /** Find a block by id anywhere in the doc; returns the block + its parent list. */
12
+ export function locate(doc, blockId) {
13
+ const search = (list) => {
14
+ for (let i = 0; i < list.length; i++) {
15
+ const b = list[i];
16
+ if (b.id === blockId)
17
+ return { block: b, siblings: list, index: i };
18
+ const found = search(b.children);
19
+ if (found)
20
+ return found;
21
+ }
22
+ return null;
23
+ };
24
+ return search(doc.children);
25
+ }
26
+ export function rawHashHex(raw) {
27
+ return sha256(raw).toString("hex");
28
+ }
29
+ /** The block that owns `children` (by reference identity), or null at top level. */
30
+ export function ownerOf(doc, children) {
31
+ if (children === doc.children)
32
+ return null;
33
+ let found = null;
34
+ const walk = (list) => {
35
+ for (const b of list) {
36
+ if (b.children === children) {
37
+ found = b;
38
+ return;
39
+ }
40
+ walk(b.children);
41
+ }
42
+ };
43
+ walk(doc.children);
44
+ return found;
45
+ }
46
+ /**
47
+ * A container's raw is the authoritative render source (03 §2.2), so a
48
+ * structural change to its children (insert/remove/move) must invalidate the
49
+ * container's cached raw. Mark the owning container dirty so renderBlock
50
+ * reconstructs it from the mutated children rather than emitting the now-stale
51
+ * raw verbatim; ancestors see it via hasDirtyDescendant and reconstruct in turn.
52
+ * Top-level siblings (doc.children) need no marking — renderDoc renders each
53
+ * top-level block directly.
54
+ */
55
+ export function markContainerDirty(doc, children) {
56
+ const owner = ownerOf(doc, children);
57
+ if (owner)
58
+ owner.dirty = true;
59
+ }
60
+ // Render the doc by splice (03 §2.2): leading trivia, optional frontmatter, then
61
+ // each top-level block + trivia. A block whose subtree contains no dirty node
62
+ // emits its retained raw verbatim. When a descendant is dirty, the block is
63
+ // re-rendered from its children (indented to their container) so nested edits
64
+ // — e.g. a task checkbox toggle inside a list — surface without disturbing
65
+ // untouched siblings.
66
+ export function renderDoc(doc) {
67
+ const out = [doc.leadingTrivia];
68
+ if (doc.frontmatterRaw !== null)
69
+ out.push(doc.frontmatterRaw);
70
+ for (const b of doc.children) {
71
+ out.push(renderBlock(b, 0));
72
+ out.push(b.trivia);
73
+ }
74
+ return out.join("");
75
+ }
76
+ function hasDirtyDescendant(b) {
77
+ if (b.dirty)
78
+ return true;
79
+ return b.children.some(hasDirtyDescendant);
80
+ }
81
+ // Render one block. Leaf or fully-clean subtree ⇒ retained raw. Otherwise
82
+ // reconstruct from children (a container with a dirty descendant).
83
+ function renderBlock(b, depth) {
84
+ if (b.children.length === 0 || !hasDirtyDescendant(b))
85
+ return b.raw;
86
+ // A list is reconstructed from its items: each item's raw already carries its
87
+ // own marker + nested content (correctly indented), so we re-mark (ordered
88
+ // lists renumber after a reorder/insert) and join with the list's own
89
+ // tight/loose separator. This matches the canonical renderer, which treats a
90
+ // top-level block's raw as the authoritative source for its whole subtree.
91
+ if (b.type === "list")
92
+ return renderList(b);
93
+ // A blockquote's `> ` markers live only in the container raw, so a rebuild
94
+ // must re-prefix every line of every child (blank separator lines become a
95
+ // bare `>`), or the quote silently degrades to plain paragraphs.
96
+ if (b.type === "blockquote")
97
+ return renderBlockquote(b, depth);
98
+ // A table's delimiter row (`| --- | --- |`) is not a child node (mdast keeps
99
+ // only header/body rows), so re-insert the retained delimiter line after the
100
+ // header row when rebuilding from rows.
101
+ if (b.type === "table")
102
+ return renderTable(b, depth);
103
+ // Other containers (a list_item outside a list): fall back to the
104
+ // child-indent rebuild. Lists are the common mutable container.
105
+ const indent = " ".repeat(depth);
106
+ return b.children.map((c) => indent + renderBlock(c, depth + 1).replace(/\n/g, "\n" + indent)).join("\n");
107
+ }
108
+ function renderBlockquote(bq, depth) {
109
+ const body = bq.children.map((c) => renderBlock(c, depth + 1)).join("\n\n");
110
+ return body.split("\n").map((ln) => (ln.length ? `> ${ln}` : ">")).join("\n");
111
+ }
112
+ function renderTable(t, depth) {
113
+ const rows = t.children.map((c) => renderBlock(c, depth + 1));
114
+ const lines = t.raw.split("\n");
115
+ const delimiter = lines.length >= 2 && /^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(lines[1]) ? lines[1] : null;
116
+ if (delimiter !== null && rows.length >= 1)
117
+ rows.splice(1, 0, delimiter);
118
+ return rows.join("\n");
119
+ }
120
+ const MARKER_RE = /^(\s*)([-*+]|\d+[.)])(\s+)/;
121
+ // Reconstruct a markdown list from its (possibly reordered / edited / added /
122
+ // removed) items. Item raws are self-contained (marker + text + nested); we
123
+ // only re-derive the leading marker so an ordered list renumbers correctly.
124
+ function renderList(list) {
125
+ const ordered = Boolean(list.attrs.ordered);
126
+ const start = typeof list.attrs.start === "number" ? list.attrs.start : 1;
127
+ const sep = list.raw.includes("\n\n") ? "\n\n" : "\n";
128
+ return list.children.map((item, i) => renderItem(item, ordered ? `${start + i}. ` : "- ")).join(sep);
129
+ }
130
+ // Render one list item under the given marker. A retained item (no strictly
131
+ // deeper change) keeps its raw, only re-marking the first line so ordered
132
+ // numbers follow the current position. An item with a dirty descendant is
133
+ // rebuilt from its child blocks, marker on the first line and continuations
134
+ // indented by the marker width.
135
+ function renderItem(item, marker) {
136
+ if (item.children.some(hasDirtyDescendant)) {
137
+ const body = item.children.map((c) => (c.type === "list" ? renderList(c) : renderBlock(c, 0))).join("\n");
138
+ return applyMarker(body, marker);
139
+ }
140
+ return applyMarker(stripMarker(item.raw), marker);
141
+ }
142
+ // Remove a leading list marker + its indentation from the first line, and the
143
+ // same indent width from continuation lines, yielding the marker-free body.
144
+ function stripMarker(raw) {
145
+ const lines = raw.split("\n");
146
+ const m = MARKER_RE.exec(lines[0] ?? "");
147
+ if (!m)
148
+ return raw;
149
+ const width = m[0].length;
150
+ return lines
151
+ .map((ln, i) => (i === 0 ? ln.slice(width) : ln.slice(0, width).trim() === "" ? ln.slice(width) : ln))
152
+ .join("\n");
153
+ }
154
+ // Prepend a marker to a marker-free body: marker on the first line, the rest
155
+ // indented by the marker width so nested content stays under the item.
156
+ function applyMarker(body, marker) {
157
+ const pad = " ".repeat(marker.length);
158
+ return body.split("\n").map((ln, i) => (i === 0 ? marker + ln : ln.length ? pad + ln : ln)).join("\n");
159
+ }
160
+ /** Ordered ids of a parent's direct children (for parent_children_hash CAS). */
161
+ export function childIds(list) {
162
+ return list.map((b) => b.id);
163
+ }
164
+ export function parentChildrenHash(list) {
165
+ return sha256(childIds(list).join(",")).toString("hex");
166
+ }
167
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ export { oqxRun, oqxRunAsync, collectSemanticPhrases, parseOqx, type OqxResult, type OqxHit, type OqxOptions, type EmbedQuery, type OqxConsumer, } from "../oqx-js/run.js";
@@ -0,0 +1,6 @@
1
+ // OQX is now served by `@omgbase/oqx` (ADR-013). This module is a thin re-export
2
+ // of the store-backed engine in ../oqx-js so every caller and the corpus keep
3
+ // importing `../oqx/run.js` unchanged. The former in-tree compiler
4
+ // (lexer/parser/lower/ir/compile/relations/scalar) is superseded.
5
+ export { oqxRun, oqxRunAsync, collectSemanticPhrases, parseOqx, } from "../oqx-js/run.js";
6
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1,23 @@
1
+ import type { DataContext } from "@omgbase/oqx";
2
+ import type { Store } from "../core/store/store.js";
3
+ import type { SemanticVec } from "../search/cel/compile.js";
4
+ export type Target = "docs" | "blocks" | "nodes" | "edges";
5
+ declare const TARGET: unique symbol;
6
+ export interface StoreContextOptions {
7
+ /** Pre-computed query vectors for `semantic("phrase")`, keyed by phrase (the
8
+ * async runner fills this; the sync path leaves it empty and a semantic() with
9
+ * no entry is a loud error). */
10
+ semanticVectors?: Map<string, SemanticVec>;
11
+ /** Rows a tier-3 plan produced, bound to the residual query's synthetic
12
+ * `ROWS_ROOT` source (planner.ts). The rows are already target-tagged store
13
+ * rows, so `get`/relations/domain-fns resolve against them unchanged. */
14
+ rowsRoot?: {
15
+ name: string;
16
+ rows: unknown[];
17
+ };
18
+ }
19
+ export declare function makeStoreContext(store: Store, repoId: string, opts?: StoreContextOptions): DataContext;
20
+ /** Tag SQL rows with their target so the store context resolves them (used by the
21
+ * tier-3 planner to hand produced rows back for residual evaluation). */
22
+ export declare function tagRows(rows: Record<string, unknown>[], target: Target): unknown[];
23
+ export { TARGET };