@ponraaj/dimple-sdk 0.0.0-alpha.1

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 (101) hide show
  1. package/dist/config.d.ts +13 -0
  2. package/dist/dimple.d.ts +101 -0
  3. package/dist/errors.d.ts +63 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +8430 -0
  6. package/dist/indexer.d.ts +43 -0
  7. package/dist/internal/config/index.d.ts +4 -0
  8. package/dist/internal/config/loader.d.ts +9 -0
  9. package/dist/internal/config/schema.d.ts +176 -0
  10. package/dist/internal/config/service.d.ts +10 -0
  11. package/dist/internal/core/core.d.ts +95 -0
  12. package/dist/internal/core/index.d.ts +3 -0
  13. package/dist/internal/core/layer.d.ts +21 -0
  14. package/dist/internal/embedder/index.d.ts +4 -0
  15. package/dist/internal/embedder/openai.d.ts +8 -0
  16. package/dist/internal/embedder/provider.d.ts +29 -0
  17. package/dist/internal/embedder/registry.d.ts +10 -0
  18. package/dist/internal/embedder/transformers.d.ts +18 -0
  19. package/dist/internal/extractor/code-extractor.d.ts +15 -0
  20. package/dist/internal/extractor/content-handler.d.ts +17 -0
  21. package/dist/internal/extractor/index.d.ts +4 -0
  22. package/dist/internal/extractor/languages/index.d.ts +24 -0
  23. package/dist/internal/extractor/languages/javascript.d.ts +22 -0
  24. package/dist/internal/extractor/languages/typescript.d.ts +20 -0
  25. package/dist/internal/extractor/models/extraction.d.ts +53 -0
  26. package/dist/internal/llm/dream-classifier.d.ts +14 -0
  27. package/dist/internal/llm/index.d.ts +4 -0
  28. package/dist/internal/llm/llm.d.ts +54 -0
  29. package/dist/internal/llm/provider.d.ts +14 -0
  30. package/dist/internal/llm/service.d.ts +29 -0
  31. package/dist/internal/logger/format.d.ts +6 -0
  32. package/dist/internal/logger/index.d.ts +4 -0
  33. package/dist/internal/logger/ring.d.ts +33 -0
  34. package/dist/internal/logger/service.d.ts +50 -0
  35. package/dist/internal/logger/types.d.ts +39 -0
  36. package/dist/internal/parser/code-parser.d.ts +12 -0
  37. package/dist/internal/parser/index.d.ts +7 -0
  38. package/dist/internal/parser/language-store.d.ts +12 -0
  39. package/dist/internal/parser/models/parseRequest.d.ts +17 -0
  40. package/dist/internal/parser/models/treeHandle.d.ts +23 -0
  41. package/dist/internal/parser/tree-sitter-engine.d.ts +15 -0
  42. package/dist/internal/retriever/hybrid.d.ts +48 -0
  43. package/dist/internal/retriever/index.d.ts +3 -0
  44. package/dist/internal/retriever/rrf.d.ts +11 -0
  45. package/dist/internal/shared/errors/index.d.ts +2 -0
  46. package/dist/internal/shared/errors/parser.d.ts +21 -0
  47. package/dist/internal/shared/errors/wasm.d.ts +28 -0
  48. package/dist/internal/shared/types/index.d.ts +1 -0
  49. package/dist/internal/shared/types/language.d.ts +3 -0
  50. package/dist/internal/store/audit.d.ts +21 -0
  51. package/dist/internal/store/db-backend.d.ts +111 -0
  52. package/dist/internal/store/drizzle-schema.d.ts +1 -0
  53. package/dist/internal/store/error.d.ts +19 -0
  54. package/dist/internal/store/fnv1a32.d.ts +14 -0
  55. package/dist/internal/store/generated-migrations.d.ts +15 -0
  56. package/dist/internal/store/graph/candidates.d.ts +93 -0
  57. package/dist/internal/store/graph/concepts.d.ts +169 -0
  58. package/dist/internal/store/graph/dream.d.ts +171 -0
  59. package/dist/internal/store/graph/errors.d.ts +25 -0
  60. package/dist/internal/store/graph/index.d.ts +15 -0
  61. package/dist/internal/store/graph/observability.d.ts +37 -0
  62. package/dist/internal/store/graph/repair.d.ts +85 -0
  63. package/dist/internal/store/graph/retrieval-post-pass.d.ts +58 -0
  64. package/dist/internal/store/graph/traversal.d.ts +39 -0
  65. package/dist/internal/store/index.d.ts +38 -0
  66. package/dist/internal/store/libsql-backend.d.ts +18 -0
  67. package/dist/internal/store/models.d.ts +122 -0
  68. package/dist/internal/store/schema/graph.d.ts +970 -0
  69. package/dist/internal/store/schema/index.d.ts +7 -0
  70. package/dist/internal/store/schema/libsql.d.ts +2356 -0
  71. package/dist/internal/store/schema/metadata.d.ts +51 -0
  72. package/dist/internal/store/schema/topics.d.ts +1966 -0
  73. package/dist/internal/store/service.d.ts +162 -0
  74. package/dist/internal/store/store.d.ts +70 -0
  75. package/dist/internal/store/topics/errors.d.ts +39 -0
  76. package/dist/internal/store/topics/index.d.ts +24 -0
  77. package/dist/internal/store/topics/jobs.d.ts +149 -0
  78. package/dist/internal/store/topics/lifecycle.d.ts +126 -0
  79. package/dist/internal/store/topics/maintenance.d.ts +110 -0
  80. package/dist/internal/store/topics/observability.d.ts +102 -0
  81. package/dist/internal/store/topics/query.d.ts +43 -0
  82. package/dist/internal/store/topics/routing.d.ts +90 -0
  83. package/dist/internal/store/topics/topic-vectors.d.ts +24 -0
  84. package/dist/internal/store/topics/tree.d.ts +181 -0
  85. package/dist/internal/wasm/grammar-store.d.ts +14 -0
  86. package/dist/internal/wasm/index.d.ts +5 -0
  87. package/dist/internal/wasm/wasm-cache.d.ts +17 -0
  88. package/dist/internal/wasm/wasm-downloader.d.ts +10 -0
  89. package/dist/internal/wasm/wasm-registry.d.ts +16 -0
  90. package/dist/internal/workflow/config.d.ts +39 -0
  91. package/dist/internal/workflow/engine.d.ts +54 -0
  92. package/dist/internal/workflow/errors.d.ts +77 -0
  93. package/dist/internal/workflow/executor.d.ts +53 -0
  94. package/dist/internal/workflow/index.d.ts +13 -0
  95. package/dist/internal/workflow/persistence.d.ts +122 -0
  96. package/dist/internal/workflow/queue-store.d.ts +5 -0
  97. package/dist/internal/workflow/queue.d.ts +298 -0
  98. package/dist/internal/workflow/registry.d.ts +57 -0
  99. package/dist/internal/workflow/runtime.d.ts +161 -0
  100. package/dist/make.d.ts +27 -0
  101. package/package.json +45 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Topic-tree maintenance jobs — underflow (merge/borrow) and sibling
3
+ * rebalance, the structural counterparts of `jobs.ts` splitting.
4
+ *
5
+ * ## mergeOrBorrow
6
+ *
7
+ * B-Tree deletion semantics applied to vector space: when a leaf drops
8
+ * below `minLeafItems`, **borrow first** (cheap, never cascades — the
9
+ * classic B-tree rotation: move the closest movable member from the
10
+ * nearest larger sibling, repeat until valid or no movable members), then
11
+ * **merge** (consumes the sibling; only when `mergedCount ≤ maxLeafItems`
12
+ * so a merge cannot immediately re-overflow into a split — the classic
13
+ * merge→split thrash). If neither is possible, the
14
+ * leaf stays underfilled (logged, status untouched) — lazy-underflow
15
+ * default policy, same as PostgreSQL's no-op on underflow.
16
+ *
17
+ * ## rebalanceSiblings
18
+ *
19
+ * Uneven siblings: when the largest sibling has more than `2×` the
20
+ * smallest, move borderline members (closer to the small centroid than
21
+ * the large one, `manual_lock = 0`, move preserves/improves both radii)
22
+ * from large to small. Gated by the growth-state cooldown
23
+ * (`cooldownUntil` + `rebalanceCooldownMs`) so repeated runs cannot churn.
24
+ *
25
+ * ## Determinism
26
+ *
27
+ * Same DB state + same config ⇒ identical moves: siblings sorted by
28
+ * `topic_key`, centroid scores tie-break lexicographically, candidate
29
+ * members sorted by `memory_id`, all CF math via the O(1) `cfInsert` /
30
+ * `cfRemove` exact updates. Audit timestamps come from the injectable
31
+ * Effect Clock (captured outside the tx closure) — observability only.
32
+ */
33
+ import { Effect } from "effect";
34
+ import { JobFailure, type Handler, type JobContext } from "./internal/workflow/index.js";
35
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
36
+ import { Service as DbBackend } from "../db-backend.ts";
37
+ import type { Interface as DbBackendInterface } from "../db-backend.ts";
38
+ import { StoreError } from "../error.ts";
39
+ import type { TopicConfig } from "../schema/topics.ts";
40
+ import type { TopicError } from "./errors.ts";
41
+ /** Result of {@link mergeOrBorrow}: what the maintenance pass did. */
42
+ export type MaintenanceOutcome = {
43
+ readonly action: "none";
44
+ } | {
45
+ readonly action: "borrowed";
46
+ readonly moves: number;
47
+ readonly donor: string;
48
+ } | {
49
+ readonly action: "merged";
50
+ readonly mergedInto: string;
51
+ readonly absorbed: string;
52
+ } | {
53
+ readonly action: "underfilled";
54
+ readonly reason: "no-borrowable" | "merge-would-overflow" | "merge-locked";
55
+ };
56
+ /** Result of {@link rebalanceSiblings}. */
57
+ export type RebalanceOutcome = {
58
+ readonly action: "none";
59
+ readonly reason: "balanced" | "cooldown" | "no-candidates";
60
+ } | {
61
+ readonly action: "rebalanced";
62
+ readonly moves: number;
63
+ };
64
+ /**
65
+ * Underflow maintenance for one leaf: borrow from the nearest larger
66
+ * sibling first, else merge with the nearest sibling (only when the merge
67
+ * stays within `maxLeafItems`), else leave the leaf underfilled (logged).
68
+ * Idempotent: a leaf at or above `minLeafItems` is a no-op.
69
+ *
70
+ * Deterministic: siblings sorted by `topic_key`, nearest by centroid score
71
+ * (lexicographic tie-break), borrowed members chosen closest to the
72
+ * underfilled centroid (tie → smaller `memory_id`), `manual_lock = 1`
73
+ * memberships never auto-move.
74
+ */
75
+ export declare const mergeOrBorrow: (topicKey: string, model: EmbeddingModelConfig, config: TopicConfig) => Effect.Effect<MaintenanceOutcome, TopicError | StoreError, DbBackend>;
76
+ /**
77
+ * Rebalance uneven siblings under one parent: when the largest sibling has
78
+ * more than `2×` the smallest, move borderline members (closer to the small
79
+ * centroid, `manual_lock = 0`, move preserves/improves both weighted radii)
80
+ * from large to small. Cooldown-gated via growth-state `cooldownUntil`.
81
+ */
82
+ export declare const rebalanceSiblings: (topicKey: string, model: EmbeddingModelConfig, config: TopicConfig) => Effect.Effect<RebalanceOutcome, TopicError | StoreError, DbBackend>;
83
+ /** Dependencies for {@link topicMergeHandler} / {@link topicRebalanceHandler}. */
84
+ export interface TopicMaintenanceHandlerDeps {
85
+ /** Resolves a model key to its config (the model registry). */
86
+ readonly getModelConfig: (modelKey: string) => EmbeddingModelConfig | undefined;
87
+ /** Tree-shape config for the maintenance decisions. */
88
+ readonly config: TopicConfig;
89
+ /** Backend captured at registration (see {@link topicSplitHandler}). */
90
+ readonly backend?: DbBackendInterface;
91
+ }
92
+ /** The `topic.merge` / `topic.rebalance` handler shape (see jobs.ts). */
93
+ export type TopicMaintenanceHandler = (payload: unknown, ctx: JobContext) => Effect.Effect<unknown, JobFailure, DbBackend>;
94
+ /**
95
+ * Factory for the `topic.merge` job handler: resolves the model, runs
96
+ * {@link mergeOrBorrow}, returns `undefined` (the definition's success
97
+ * schema is `Schema.Void` — the outcome lives in the DB).
98
+ */
99
+ export declare function topicMergeHandler(deps: TopicMaintenanceHandlerDeps & {
100
+ readonly backend: DbBackendInterface;
101
+ }): Handler;
102
+ export declare function topicMergeHandler(deps: TopicMaintenanceHandlerDeps): TopicMaintenanceHandler;
103
+ /**
104
+ * Factory for the `topic.rebalance` job handler: resolves the model, runs
105
+ * {@link rebalanceSiblings}, returns `undefined`.
106
+ */
107
+ export declare function topicRebalanceHandler(deps: TopicMaintenanceHandlerDeps & {
108
+ readonly backend: DbBackendInterface;
109
+ }): Handler;
110
+ export declare function topicRebalanceHandler(deps: TopicMaintenanceHandlerDeps): TopicMaintenanceHandler;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Topic-tree observability: metrics, change-log queries, and health checks.
3
+ *
4
+ * All three functions are read-only, Effect-based, take `DbBackend` from the
5
+ * environment, and produce deterministically ordered output (never
6
+ * wall-clock, never randomness):
7
+ *
8
+ * - `topicMetrics` — one-shot aggregate snapshot of a model's tree
9
+ * (size, shape, overflow/deferral pressure, rejection total, last op).
10
+ * - `topicChangeLog` — newest-first audit trail (the append-only log is the
11
+ * replay/repair source).
12
+ * - `topicHealth` — structural invariant sweep; violations are human-readable
13
+ * strings, `warnCount` = violation count.
14
+ *
15
+ * ## Scope note
16
+ *
17
+ * `topic_change_log` has no `model_key` column (schema/topics.ts), so
18
+ * change-log queries scope through the model's topic keys via a subquery —
19
+ * the same pattern as `Store.deleteTree`.
20
+ *
21
+ * ## Complexity (current scale)
22
+ *
23
+ * - `topicMetrics`: one `topics` scan (`O(T)` rows, T = O(N/L)) + one
24
+ * `topic_growth_state` aggregate + one change-log head read.
25
+ * - `topicHealth`: one `topics` scan + ONE grouped `topic_memberships` count
26
+ * query (the current-scale equivalent of COUNT-per-topic; a production
27
+ * index would maintain per-topic counts instead).
28
+ *
29
+ * ## Determinism
30
+ *
31
+ * Ordering derives from persisted state only: `leafMemberCounts` is sorted
32
+ * ascending, change-log rows by `id` DESC (PK order, ties impossible). No
33
+ * wall-clock in any ordering decision.
34
+ */
35
+ import { Effect } from "effect";
36
+ import { Service as DbBackend } from "../db-backend.ts";
37
+ import type { TopicConfig, TopicChangeLog } from "../schema/topics.ts";
38
+ import type { TopicError } from "./errors.ts";
39
+ import type { StoreError } from "../error.ts";
40
+ /** One-shot aggregate snapshot of a model's tree (see {@link topicMetrics}). */
41
+ export interface TopicMetrics {
42
+ readonly totalTopics: number;
43
+ readonly leaves: number;
44
+ readonly internal: number;
45
+ /** Maximum `depth` over all topic rows (0 = root only). */
46
+ readonly depth: number;
47
+ /** Member counts of every leaf, sorted ascending (regardless of status). */
48
+ readonly leafMemberCounts: ReadonlyArray<number>;
49
+ /** Leaves with `memberCount > config.maxLeafItems` (split-job pressure). */
50
+ readonly overflowCandidates: number;
51
+ /** Topics with status `split_deferred` (gate rejected, awaiting backstop). */
52
+ readonly deferredLeaves: number;
53
+ /** Sum of `split_rejected_count` over the model's growth-state rows. */
54
+ readonly splitRejectedTotal: number;
55
+ /** Newest change-log entry (by id), or null when the log is empty. */
56
+ readonly lastOperation: {
57
+ readonly operation: string;
58
+ readonly createdAt: number;
59
+ } | null;
60
+ }
61
+ /** Options for {@link topicChangeLog}. */
62
+ export interface TopicChangeLogOptions {
63
+ /** Only rows with this operation (e.g. `"split"`, `"create"`). */
64
+ readonly operation?: string;
65
+ /** Maximum rows returned, newest first (default 50). */
66
+ readonly limit?: number;
67
+ }
68
+ /** Structural health of a model's tree (see {@link topicHealth}). */
69
+ export interface TopicHealth {
70
+ /** Human-readable invariant violations; empty = healthy. */
71
+ readonly invariantViolations: ReadonlyArray<string>;
72
+ /** `invariantViolations.length`. */
73
+ readonly warnCount: number;
74
+ }
75
+ /**
76
+ * Aggregate metrics for one model's tree. Read-only; the caller decides what
77
+ * to act on (repair tooling, dashboards, overflow autoscaling).
78
+ */
79
+ export declare const topicMetrics: (modelKey: string, config: TopicConfig) => Effect.Effect<TopicMetrics, TopicError | StoreError, DbBackend>;
80
+ /**
81
+ * Newest-first audit trail for a model's tree. Rows are ordered by `id` DESC
82
+ * (PK order — the append-only log's natural recency), limited to
83
+ * `opts.limit` (default 50), optionally filtered by `opts.operation`
84
+ * (exact match on the `operation` column).
85
+ */
86
+ export declare const topicChangeLog: (modelKey: string, opts?: TopicChangeLogOptions) => Effect.Effect<ReadonlyArray<TopicChangeLog>, TopicError | StoreError, DbBackend>;
87
+ /**
88
+ * Structural invariant sweep for one model's tree. Reports every violation as
89
+ * a human-readable string; an empty `invariantViolations` means healthy.
90
+ *
91
+ * Checks:
92
+ * 1. Every leaf `memberCount <= maxLeafItems + slack`, where
93
+ * `slack = max(2, ceil(splitDeferredHardLimit / 2) − maxLeafItems)` — the
94
+ * same allowance the split acceptance gate grants a forced split's child.
95
+ * 2. Every internal topic has `childCount >= 2`. The reserved `__root__`
96
+ * anchor is exempt: it is seeded with `childCount 0` and routing/split
97
+ * never maintain it (the root's child set lives in `topic_relations`).
98
+ * 3. Leaf `memberCount` agrees with the actual `topic_memberships` row count
99
+ * (one grouped COUNT query — the current-scale stand-in for a
100
+ * maintained per-topic counter).
101
+ */
102
+ export declare const topicHealth: (modelKey: string, config: TopicConfig) => Effect.Effect<TopicHealth, TopicError | StoreError, DbBackend>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Deterministic query routing: leaf selection for topic-scoped retrieval.
3
+ *
4
+ * Beam descent from `__root__`: at each level score the children of the
5
+ * current frontier, keep the top-`k` (`k = opts.beamWidth ?? config.beamWidth`,
6
+ * default 3), descend. Stops when the whole frontier is leaves. An empty
7
+ * result means the tree has no leaves (immature or corrupt) — the caller
8
+ * falls back to an unscoped search.
9
+ *
10
+ * ## Determinism
11
+ *
12
+ * - Topic rows are ordered by `topic_key` asc; children are sorted by
13
+ * `topic_key` asc; topic vectors come from `fetchTopicVectors` (ordered by
14
+ * `topic_key` asc).
15
+ * - `scoreChildren` breaks score ties to the smaller index, which IS the
16
+ * lexicographic tie-break (same argument as `routing.ts`).
17
+ * - No randomness, no wall-clock timestamps, no unordered iteration. Same DB
18
+ * state + same query ⇒ identical leaf keys.
19
+ *
20
+ * ## Complexity
21
+ *
22
+ * `O(D·B·C·d)` (D = maxDepth, B = beamWidth, C = children per node) plus one
23
+ * bounded read of the model's topics rows, relations and centroids. Final
24
+ * ranking still runs in libSQL (`vectorSearch` with `topic_key IN (...)`).
25
+ */
26
+ import { Effect } from "effect";
27
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
28
+ import { Service as DbBackend } from "../db-backend.ts";
29
+ import type { TopicConfig } from "../schema/topics.ts";
30
+ import type { TopicError } from "./errors.ts";
31
+ import type { StoreError } from "../error.ts";
32
+ /** Options for {@link resolveTopics}. */
33
+ export interface ResolveTopicsOptions {
34
+ /** Number of children kept per level (default: `config.beamWidth`). */
35
+ readonly beamWidth?: number;
36
+ }
37
+ /**
38
+ * Resolve the leaf topics a query should search: `readonly string[]` of
39
+ * `topic_key`s, ordered by score (descending; ties lexicographic). An empty
40
+ * array means the tree has no leaves — the caller falls back to an unscoped
41
+ * search.
42
+ */
43
+ export declare const resolveTopics: (queryVector: ReadonlyArray<number>, model: EmbeddingModelConfig, config: TopicConfig, opts?: ResolveTopicsOptions) => Effect.Effect<ReadonlyArray<string>, TopicError | StoreError, DbBackend>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Deterministic topic-tree seeding + insert routing.
3
+ *
4
+ * ## Contracts
5
+ *
6
+ * - `ensureTopicTree` is an idempotent seed: creates the two reserved topics
7
+ * (`__root__` internal, `__unassigned_active__` leaf), their `parent_child`
8
+ * relation, growth-state rows and exactly one `create` change-log entry —
9
+ * all in one transaction, scoped by `model_key` (every model's
10
+ * tree has its own reserved rows — identity is the `(topic_key, model_key)`
11
+ * composite PK). Reserved keys sort before any `t_<hash>` key
12
+ * lexicographically (`_` = 0x5F < `t` = 0x74), which the descent relies on
13
+ * for stable tie-breaking.
14
+ * - `routeInsert` runs its Drizzle writes (membership insert, leaf CF, ancestor
15
+ * CF) in ONE transaction; the derived sum-vector upserts run immediately
16
+ * after commit (the backend is not transaction-aware — see below). It never
17
+ * writes change-log entries (only structural operations do).
18
+ * - Determinism: given the same DB state, the same `(memoryId, vector, model,
19
+ * config)` always lands in the same leaf. Children are sorted by
20
+ * `topic_key` and score ties break lexicographically (smaller key wins).
21
+ * No randomness,
22
+ * no insertion-order-dependent structure. Audit
23
+ * timestamps (`created_at`/`updated_at`) come from the injectable Effect
24
+ * Clock — they are observability data only and never influence routing.
25
+ *
26
+ * ## Transactions vs the backend (documented limitation)
27
+ *
28
+ * `upsertTopicVector` / `fetchTopicVectors` are DbBackend raw-SQL calls on the
29
+ * client connection, NOT transaction-aware. Drizzle's libsql transaction runs
30
+ * on a **separate connection** (`client.transaction()`), so calling them
31
+ * inside the tx callback fails with `SQLITE_BUSY`. The code therefore
32
+ * keeps every Drizzle write (membership + `topics` CF rows) in the single
33
+ * transaction and runs the vector-table upserts immediately AFTER commit.
34
+ * Consequence: the derived sum vectors are eventually consistent with the
35
+ * strictly-consistent membership — a crash between commit and upsert
36
+ * leaves a stale centroid, never an orphaned membership. Single-writer local
37
+ * file DB, so the pre-tx read set is stable; if the backend grows
38
+ * transaction-aware vector methods, move the upserts onto the tx.
39
+ */
40
+ import { Effect } from "effect";
41
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
42
+ import { Service as DbBackend } from "../db-backend.ts";
43
+ import type { TopicConfig } from "../schema/topics.ts";
44
+ import type { TopicError } from "./errors.ts";
45
+ import type { StoreError } from "../error.ts";
46
+ /** Reserved tree root — internal router, depth 0. */
47
+ export declare const ROOT_TOPIC_KEY = "__root__";
48
+ /** Reserved overflow/seed leaf under the root — depth 1. */
49
+ export declare const UNASSIGNED_ACTIVE_TOPIC_KEY = "__unassigned_active__";
50
+ /**
51
+ * Idempotent tree seed. If the model's `__root__` exists the tree is already
52
+ * seeded and this is a no-op (the existence check runs INSIDE the transaction
53
+ * and is scoped by `model_key` — (multi-model) the reserved keys are the same
54
+ * strings in every model's tree, and the `(topic_key, model_key)` composite PK
55
+ * is what separates them; two concurrent seeds of the same model cannot
56
+ * double-insert). Otherwise one transaction creates:
57
+ * `__root__` (internal, depth 0), `__unassigned_active__` (leaf, depth 1,
58
+ * `parent_child` under root), growth-state rows for both, and one `create`
59
+ * change-log entry.
60
+ *
61
+ * Also ensures the per-model topic vector table exists (idempotent
62
+ * `CREATE TABLE IF NOT EXISTS`) — `routeInsert` upserts into it, so the seed
63
+ * is the natural lifecycle point.
64
+ */
65
+ export declare const ensureTopicTree: (model: EmbeddingModelConfig, config: TopicConfig) => Effect.Effect<{
66
+ rootKey: string;
67
+ unassignedKey: string;
68
+ }, TopicError | StoreError, DbBackend>;
69
+ /**
70
+ * Route one memory (its embedding `vector`) into the model's tree and update
71
+ * the affected CF-triplets:
72
+ *
73
+ * 1. Route: greedy descent from `__root__` through the best child centroid,
74
+ * capped at `maxDepth` (lexicographic tie-break).
75
+ * 2. ONE transaction: insert the `topic_memberships` row, update the leaf CF
76
+ * (`memberCount + 1`, `sumSquares += x·x`, sum vector `+= x` — O(1) exact
77
+ * via `cfInsert`), and walk the ancestors (`memberCount + 1`, sum vector
78
+ * `+= x` per level — exact O(depth · d)).
79
+ * 3. After commit: write the derived sum vectors to the per-model topic vector
80
+ * table (backend upserts — see module docs).
81
+ * 4. `overflow = memberCount > maxLeafItems` after the update (the caller
82
+ * decides whether to enqueue a structural job).
83
+ *
84
+ * Deterministic given the same DB state (see module docs). Does NOT write
85
+ * change-log entries — only structural operations do.
86
+ */
87
+ export declare const routeInsert: (memoryId: string, vector: ReadonlyArray<number>, model: EmbeddingModelConfig, config: TopicConfig) => Effect.Effect<{
88
+ topicKey: string;
89
+ overflow: boolean;
90
+ }, TopicError | StoreError, DbBackend>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared topic-vector-table helpers for the structural jobs (split, merge,
3
+ * borrow, rebalance). The per-model `topic_vec_<dims>_<model>` tables are
4
+ * dynamic (not Drizzle tables), so every writer goes through this raw-SQL
5
+ * helper — one canonical place for the table name + conversion function.
6
+ */
7
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
8
+ import { type SQLWrapper } from "drizzle-orm";
9
+ /** Per-model topic vector table name (mirrors libsql-backend.ts — canonical). */
10
+ export declare const topicVectorTableName: (model: EmbeddingModelConfig) => string;
11
+ /** vectorType → libSQL conversion function for the sum-vector column. */
12
+ export declare const VECTOR_CONVERT_FNS: Readonly<Record<string, string>>;
13
+ /** Anything with a `.run(sql)` — a Drizzle tx or the db handle. */
14
+ export interface SqlRunner {
15
+ readonly run: (query: string | SQLWrapper<unknown>) => unknown;
16
+ }
17
+ /** Delete one topic's sum-vector row (merge absorbs the sibling's row). */
18
+ export declare const deleteTopicVectorRow: (runner: SqlRunner, model: EmbeddingModelConfig, topicKey: string) => Promise<void>;
19
+ /**
20
+ * Upsert one topic's sum vector. `ls` is the CF-triplet LS (sum of member
21
+ * vectors) as a plain Float32Array. Works inside a transaction (structural
22
+ * ops) or directly on the db handle (repair paths).
23
+ */
24
+ export declare const writeTopicVector: (runner: SqlRunner, model: EmbeddingModelConfig, topicKey: string, ls: Float32Array) => Promise<void>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Pure, deterministic topic-tree algorithms.
3
+ *
4
+ * All functions in this module are synchronous, side-effect free, and never
5
+ * mutate their inputs. They operate on plain `Float32Array` vectors.
6
+ *
7
+ * ## Invariant: vectors are L2-normalized
8
+ *
9
+ * The embedder normalizes every vector (`transformers.js`, `normalize: true`),
10
+ * so for every stored vector `||x|| = 1` (or the zero vector, in degenerate
11
+ * cases). Consequently **cosine similarity equals the dot product**, and all
12
+ * similarity comparisons in this module are plain dot products. A zero vector
13
+ * behaves as a "neutral" item: its dot product with anything is `0`.
14
+ *
15
+ * ## Determinism contract
16
+ *
17
+ * - No randomness, no wall-clock timestamps, no insertion-order-dependent
18
+ * structure.
19
+ * - Ties always break to the SMALLER index (farthest-pair candidate, group
20
+ * assignment, child ranking).
21
+ * - Same input + same config ⇒ bit-identical output.
22
+ *
23
+ * ## Complexity
24
+ *
25
+ * `n` = vectors in a leaf (bounded by `maxLeafItems` / split-deferred hard
26
+ * limit), `d` = embedding dimensions. Split machinery is `O(passes · n · d)`
27
+ * with `n ≤ 512`.
28
+ */
29
+ import type { TopicConfig } from "../schema/topics.ts";
30
+ /**
31
+ * BIRCH CF-triplet: sufficient statistics of a vector set.
32
+ * `count` = N, `ls` = sum vector, `ss` = sum of squared norms.
33
+ * Supports O(1) exact insert/remove and closed-form centroid/radius — no
34
+ * vector scans for aggregates.
35
+ */
36
+ export interface CfTriplet {
37
+ readonly count: number;
38
+ readonly ls: Float32Array;
39
+ readonly ss: number;
40
+ }
41
+ /**
42
+ * Build the CF-triplet of a vector set. All vectors must share one length;
43
+ * an empty input yields a zero-dimension triplet (`count = 0`).
44
+ */
45
+ export declare const cfFromVectors: (vectors: readonly Float32Array[]) => CfTriplet;
46
+ /**
47
+ * O(1) exact insert of vector `x` into a CF-triplet. Returns a new triplet;
48
+ * neither input is mutated.
49
+ */
50
+ export declare const cfInsert: (cf: CfTriplet, x: Float32Array) => CfTriplet;
51
+ /**
52
+ * O(1) exact removal of vector `x` from a CF-triplet. `count` clamps at 0 and
53
+ * `ss` clamps at 0 (float subtraction can round slightly below). Returns a
54
+ * new triplet; neither input is mutated.
55
+ */
56
+ export declare const cfRemove: (cf: CfTriplet, x: Float32Array) => CfTriplet;
57
+ /**
58
+ * Centroid (mean vector) of a CF-triplet: `ls / count`. An empty triplet
59
+ * (`count = 0`) yields the zero vector of the same dimension.
60
+ */
61
+ export declare const centroidOf: (cf: CfTriplet) => Float32Array;
62
+ /**
63
+ * RMS radius of a CF-triplet: `sqrt(max(ss/count - |mean|^2, 0))` — the
64
+ * exact root-mean-square deviation of members from the centroid, computed
65
+ * from the CF alone (no vector scan). Empty triplet ⇒ `0`.
66
+ */
67
+ export declare const radiusOf: (cf: CfTriplet) => number;
68
+ /**
69
+ * BIRCH-style weighted radius of a CF-triplet: `ss - |ls|^2 / count`, the
70
+ * sum of squared deviations from the centroid, exact from the CF alone.
71
+ * By the variance decomposition this equals the parent's weighted radius
72
+ * minus the between-group term for any two-group partition — the quantity
73
+ * Lloyd refinement monotonically decreases (exact arithmetic; float32
74
+ * accumulation noise is < 1e-3 relative). Empty triplet ⇒ `0`.
75
+ */
76
+ export declare const weightedRadiusOf: (cf: CfTriplet) => number;
77
+ /**
78
+ * Content-canonical permutation of `vectors`: indices sorted by
79
+ * lexicographic coordinate order (stable — equal vectors keep input order).
80
+ * `sorted[i] = vectors[order[i]!]`. The whole split pipeline runs on the
81
+ * canonical order so its output is a function of the vector SET, not of the
82
+ * caller's array order (random UUID-ordered memberships must not change the
83
+ * tree shape — the benchmark flake root cause).
84
+ */
85
+ export declare const canonicalOrder: (vectors: readonly Float32Array[]) => readonly number[];
86
+ /**
87
+ * Deterministic farthest-first seed pair: `a = 0` (first vector of the
88
+ * CANONICAL order — callers must sort via {@link canonicalOrder} first),
89
+ * then `b` = the vector with the lowest dot product against `a` (farthest
90
+ * under the normalized-vector invariant). Ties break to the smaller index.
91
+ * For fewer than 2 vectors returns `{ a: 0, b: 0 }` (degenerate — callers
92
+ * guard on counts before splitting). Never mutates input.
93
+ */
94
+ export declare const farthestPair: (vectors: readonly Float32Array[]) => {
95
+ a: number;
96
+ b: number;
97
+ };
98
+ /**
99
+ * Assign every vector to group 0 or 1 by higher dot product against the
100
+ * seed vectors at `seedA` / `seedB`; ties go to group 0 (smaller index).
101
+ * Returns a fresh `Uint8Array` (`0` = closer to `seedA`, `1` = closer to
102
+ * `seedB`). Never mutates inputs.
103
+ */
104
+ export declare const assignNearest: (vectors: readonly Float32Array[], seedA: number, seedB: number) => Uint8Array;
105
+ /**
106
+ * Deterministic Lloyd refinement: recompute each group's centroid, reassign
107
+ * every vector to the group with the higher dot product (tie ⇒ smaller group
108
+ * index, i.e. 0), repeat for `passes` iterations. Stops early at a fixpoint —
109
+ * further passes are no-ops, so the result is identical either way. Returns
110
+ * the final groups plus both group CF-triplets.
111
+ *
112
+ * Monotone: the sum of weighted radii never increases in exact arithmetic.
113
+ * Comparisons use the float64 division of the float32-accumulated sum
114
+ * `ls / count` (not the float32-rounded {@link centroidOf}) so that each
115
+ * reassignment is a true SSE minimization — the classic Lloyd chain
116
+ * `WR(new) ≤ WR(new, μ_old) ≤ WR(old)` holds in float64. The contract-mandated
117
+ * float32 `ls` accumulation perturbs the *measured* WR by < 1e-3 relative
118
+ * (empirically ≤ 5e-4 over 2000 seeds).
119
+ *
120
+ * Never mutates inputs.
121
+ */
122
+ export declare const lloydRefine: (vectors: readonly Float32Array[], groups: Uint8Array, passes: number) => {
123
+ groups: Uint8Array;
124
+ leftCf: CfTriplet;
125
+ rightCf: CfTriplet;
126
+ };
127
+ /** Outcome of {@link splitPlan} — accepted splits plus the acceptance gate diagnostics. */
128
+ export interface SplitPlanResult {
129
+ readonly accepted: boolean;
130
+ readonly reason: "ok" | "unbalanced" | "no-improvement";
131
+ readonly groups: Uint8Array;
132
+ readonly leftCount: number;
133
+ readonly rightCount: number;
134
+ readonly leftWeightedRadius: number;
135
+ readonly rightWeightedRadius: number;
136
+ readonly parentWeightedRadius: number;
137
+ }
138
+ /**
139
+ * Deterministic 2-way split plan for one leaf: content-canonical ordering →
140
+ * farthest-pair seeds → nearest-seed assignment → Lloyd refinement
141
+ * (`config.lloydPasses` passes). Accepted iff both child counts reach
142
+ * `config.minLeafItems` AND the summed child weighted radii are strictly
143
+ * below the parent's (a non-degenerate split strictly reduces weighted
144
+ * radius by the between-group term `(n₁n₂/n)·|μ₁−μ₂|²`; equality —
145
+ * "no-improvement" — means the centroids coincide). Reason precedence:
146
+ * `"unbalanced"` (count gate) before `"no-improvement"` (radius gate).
147
+ *
148
+ * ## Determinism (v3)
149
+ *
150
+ * The pipeline first reorders the input via {@link canonicalOrder}, so the
151
+ * returned plan is a function of the vector SET only — never of the
152
+ * caller's array order. `groups` is un-permuted back to the CALLER's index
153
+ * space before returning (index `i` corresponds to `vectors[i]`), so
154
+ * callers map group assignments straight onto their own member ids.
155
+ *
156
+ * Remaining order-dependence: bit-identical vectors keep their input
157
+ * relative order (stable sort) and always score equally, so they land in
158
+ * the same group; only the float32 accumulation inside `cfOfGroup` can
159
+ * round differently for them (≤ 1 ulp on `ls`) — it cannot flip a distinct
160
+ * vector's assignment unless that vector is itself within 1 ulp of a tie.
161
+ *
162
+ * Never mutates inputs.
163
+ */
164
+ export declare const splitPlan: (vectors: readonly Float32Array[], config: TopicConfig) => SplitPlanResult;
165
+ /**
166
+ * Score child centroids against a query vector: dot products, sorted
167
+ * descending; ties break to the smaller index. Returns a fresh array of
168
+ * `{ index, score }` pairs (index = position in `centroids`). Never mutates
169
+ * inputs.
170
+ */
171
+ export declare const scoreChildren: (centroids: readonly Float32Array[], query: Float32Array) => Array<{
172
+ index: number;
173
+ score: number;
174
+ }>;
175
+ /**
176
+ * Validate a `TopicConfig` against the structural invariants of the tree.
177
+ * Returns an array of violation strings (empty when valid). Warnings — such
178
+ * as `minLeafItems * 2 > maxLeafItems`, which makes splits unsatisfiable at
179
+ * a full leaf — are included so callers can log them without failing.
180
+ */
181
+ export declare const validateConfig: (config: TopicConfig) => ReadonlyArray<string>;
@@ -0,0 +1,14 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { GrammarProvisionError } from "@dimple/shared/errors";
3
+ import { Service as TreeSitterRegistry } from "./wasm-registry";
4
+ import { Service as WasmDownloader } from "./wasm-downloader";
5
+ import { Service as WasmCache } from "./wasm-cache";
6
+ export interface Interface {
7
+ readonly ensure: (lang: string) => Effect.Effect<string, GrammarProvisionError>;
8
+ }
9
+ declare const Service_base: Context.ServiceClass<Service, "@dimple/wasm/GrammarStore", Interface>;
10
+ export declare class Service extends Service_base {
11
+ }
12
+ export declare const layer: Layer.Layer<Service, never, TreeSitterRegistry | WasmDownloader | WasmCache>;
13
+ export declare const allLive: Layer.Layer<TreeSitterRegistry | WasmDownloader | WasmCache | Service, never, never>;
14
+ export {};
@@ -0,0 +1,5 @@
1
+ export { Service as TreeSitterRegistry } from "./wasm-registry";
2
+ export type { GrammarMetaData } from "./wasm-registry";
3
+ export { Service as WasmDownloader } from "./wasm-downloader";
4
+ export { Service as WasmCache, layer as wasmCacheLayer } from "./wasm-cache";
5
+ export { Service as GrammarStore, layer as grammarStoreLayer, allLive as GrammarStoreAllLive, } from "./grammar-store";
@@ -0,0 +1,17 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { WasmCacheError } from "@dimple/shared/errors";
3
+ /**
4
+ * Grammar WASM cache — downloads are staged to a temp file and renamed
5
+ * into place (an interrupted download never leaves a corrupt parser).
6
+ * Plain `node:fs` (NOT @effect/platform-bun): the bundled SDK runs on
7
+ * Node too (the CLI's shebang spawns node), so Bun-specific filesystem
8
+ * layers are off the table.
9
+ */
10
+ export interface Interface {
11
+ readonly ensure: (lang: string, version: string, download: Effect.Effect<Uint8Array>) => Effect.Effect<string, WasmCacheError>;
12
+ }
13
+ declare const Service_base: Context.ServiceClass<Service, "@dimple/wasm/WasmCache", Interface>;
14
+ export declare class Service extends Service_base {
15
+ }
16
+ export declare const layer: Layer.Layer<Service, never, never>;
17
+ export {};
@@ -0,0 +1,10 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { WasmDownloadError } from "@dimple/shared/errors";
3
+ export interface Interface {
4
+ readonly download: (url: string) => Effect.Effect<Uint8Array, WasmDownloadError>;
5
+ }
6
+ declare const Service_base: Context.ServiceClass<Service, "@dimple/wasm/WasmDownloader", Interface>;
7
+ export declare class Service extends Service_base {
8
+ static readonly layer: Layer.Layer<Service, never, never>;
9
+ }
10
+ export {};
@@ -0,0 +1,16 @@
1
+ import { Context, Effect, Layer, Schema } from "effect";
2
+ import { RegistryError } from "@dimple/shared/errors";
3
+ declare const GrammarMetaDataSchema: Schema.Struct<{
4
+ readonly lang: Schema.String;
5
+ readonly version: Schema.String;
6
+ readonly url: Schema.String;
7
+ }>;
8
+ export type GrammarMetaData = typeof GrammarMetaDataSchema.Type;
9
+ export interface Interface {
10
+ readonly resolve: (lang: string) => Effect.Effect<GrammarMetaData, RegistryError>;
11
+ }
12
+ declare const Service_base: Context.ServiceClass<Service, "@dimple/wasm/TreeSitterRegistry", Interface>;
13
+ export declare class Service extends Service_base {
14
+ static readonly layer: Layer.Layer<Service, never, never>;
15
+ }
16
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Job runtime configuration — the `jobs` section of `dimple.jsonc`.
3
+ *
4
+ * All fields optional; `resolveJobsConfig` fills the documented defaults.
5
+ * Consumed by the deployment wiring (executor mode selection, worker
6
+ * concurrency, queue lease/attempt tuning) — the facade itself reads its
7
+ * options from the layer composition, not from this schema.
8
+ */
9
+ import { Schema } from "effect";
10
+ export declare const JobsConfig: Schema.Struct<{
11
+ /** libSQL URL for the dedicated jobs database. */
12
+ readonly dbUrl: Schema.optional<Schema.String>;
13
+ /** `background` (worker layers) | `inline` (submit-scope worker) | `external` (cron drain).
14
+ * RESERVED: the core composition currently installs background workers
15
+ * only (the config package parses it but nothing switches on it yet). */
16
+ readonly execution: Schema.optional<Schema.Literals<readonly ["background", "inline", "external"]>>;
17
+ /** Worker concurrency per queue. */
18
+ readonly concurrency: Schema.optional<Schema.Number>;
19
+ /** Queue item lease duration in ms (→ `PersistedQueue` `lockExpiration`).
20
+ * RESERVED: not yet wired to the executor. */
21
+ readonly leaseTimeoutMs: Schema.optional<Schema.Number>;
22
+ /** Max attempts per queue item before it is dropped (→ `take maxAttempts`). */
23
+ readonly maxAttempts: Schema.optional<Schema.refine<number, Schema.Number>>;
24
+ /** External-drain poll window hint in ms. RESERVED: not yet wired. */
25
+ readonly pollIntervalMs: Schema.optional<Schema.Number>;
26
+ }>;
27
+ export interface JobsConfig extends Schema.Schema.Type<typeof JobsConfig> {
28
+ }
29
+ /** Documented defaults — the source of truth for `resolveJobsConfig`. */
30
+ export declare const JobsConfigDefaults: {
31
+ dbUrl: string;
32
+ execution: "background";
33
+ concurrency: number;
34
+ leaseTimeoutMs: number;
35
+ maxAttempts: number;
36
+ pollIntervalMs: number;
37
+ };
38
+ /** Fills every optional field with its documented default. */
39
+ export declare const resolveJobsConfig: (config: JobsConfig) => Required<JobsConfig>;