@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,15 @@
1
+ /**
2
+ * GENERATED — do not edit by hand. Run `bun run scripts/generate-migrations.ts`
3
+ * after changing packages/store/drizzle. The migration SQL is compiled into
4
+ * the bundle (opencode's migrations-as-code pattern): runtime migrations
5
+ * never read a folder from disk.
6
+ */
7
+ export interface EmbeddedMigration {
8
+ readonly name: string;
9
+ /** sha256 of the SQL — identical to drizzle-kit's journal hashes, so
10
+ * databases migrated by the folder-based runner skip already-applied
11
+ * entries and new inserts stay journal-compatible. */
12
+ readonly hash: string;
13
+ readonly sql: string;
14
+ }
15
+ export declare const EMBEDDED_MIGRATIONS: readonly EmbeddedMigration[];
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Write-path edge candidates — the pure, deterministic rules that scan a
3
+ * routed topic leaf after a memory's embedding lands and propose
4
+ * `artifact_edges` rows.
5
+ *
6
+ * No Effect, no DB: the store loads the leaf members and this module
7
+ * decides. Every rule is a pure function of the inputs (plus the conflict
8
+ * bands), and the output is deterministic — members are sorted by memory
9
+ * id and each member yields at MOST ONE candidate (the strongest
10
+ * applicable rule wins).
11
+ *
12
+ * ## Rules (per leaf member, evaluated in priority order)
13
+ *
14
+ * 1. Identity — the member has the same logical key as the new memory:
15
+ * CONFIRMED `memory_supersedes` (confidence 0.5). Same-key memories are
16
+ * the same logical artifact, so the newer one wins immediately; the
17
+ * store flips the old memory to `superseded` in the same transaction as
18
+ * the edge insert. Pending candidates never mutate state — this rule is
19
+ * the one exception because the key match is exact, not a similarity
20
+ * guess.
21
+ * 2. Near-duplicate — cosine similarity above the `nearDuplicate` band
22
+ * edge: PENDING `memory_supersedes` (confidence = raw cosine). Waits
23
+ * for confirmation; a false positive is rejected, never harmful.
24
+ * 3. Conflict band — cosine inside `(conflictLow, nearDuplicate]` AND the
25
+ * two memories are related (same logical-key family — version-suffixed
26
+ * variants of one key — or a shared metadata `topics` tag): PENDING
27
+ * `memory_contradicts` (confidence = raw cosine). The metadata blob is
28
+ * parsed defensively — a malformed blob matches nothing.
29
+ *
30
+ * Direction is canonical: the OLDER memory (by `createdAt`; a timestamp
31
+ * tie keeps the existing member, so the just-written memory never flips
32
+ * itself) is `fromMemoryId` — the endpoint confirmation supersedes — and
33
+ * the newer one is `toMemoryId`.
34
+ */
35
+ import type { EdgeKind, EdgeSource } from "../schema/graph.ts";
36
+ /** One leaf member as seen by the candidate rules. */
37
+ export interface CandidateMember {
38
+ readonly id: string;
39
+ readonly vector: Float32Array;
40
+ readonly createdAt: number;
41
+ readonly logicalKey?: string;
42
+ readonly metadata?: string | null;
43
+ }
44
+ /** Which rule produced a candidate (the provenance on `EdgeCandidate`). */
45
+ export type CandidateKind = "near-duplicate" | "conflict-band" | "identity";
46
+ /** One rule-derived edge proposal from `leafLocalCandidates`. */
47
+ export interface EdgeCandidate {
48
+ readonly fromMemoryId: string;
49
+ readonly toMemoryId: string;
50
+ readonly edgeKind: "memory_supersedes" | "memory_contradicts";
51
+ readonly source: "rule";
52
+ readonly status: "pending" | "confirmed";
53
+ readonly confidence: number;
54
+ readonly reason: CandidateKind;
55
+ }
56
+ /** One pending candidate surfaced by `Store.graphCandidates` (an
57
+ * `artifact_edges` row — the status is `pending` by construction). */
58
+ export interface EdgeCandidateInfo {
59
+ readonly id: string;
60
+ readonly fromMemoryId: string;
61
+ readonly toMemoryId: string;
62
+ readonly edgeKind: EdgeKind;
63
+ readonly source: EdgeSource;
64
+ readonly confidence: number | null;
65
+ readonly createdAt: number;
66
+ }
67
+ /** One isolated memory (no live incident edge on either endpoint). */
68
+ export interface IsolatedMemory {
69
+ readonly id: string;
70
+ readonly createdAt: number;
71
+ }
72
+ /** `Store.graphCandidates` result: the newest pending candidates plus the
73
+ * isolated-memory survey (count + oldest examples). */
74
+ export interface GraphCandidatesResult {
75
+ readonly pending: readonly EdgeCandidateInfo[];
76
+ readonly isolatedCount: number;
77
+ readonly oldestIsolated: readonly IsolatedMemory[];
78
+ }
79
+ /** Options for `Store.graphCandidates`. */
80
+ export interface GraphCandidatesOptions {
81
+ /** Max pending candidates returned (clamped to [1, 200]; default 50). */
82
+ readonly limit?: number;
83
+ }
84
+ /**
85
+ * The leaf-local candidate scan: compare the new memory against its
86
+ * leaf-mates (per the module rules) and return at most one candidate per
87
+ * member, in member-id order (deterministic — the input order is
88
+ * irrelevant). The new memory itself is never a candidate target.
89
+ */
90
+ export declare const leafLocalCandidates: (newId: string, newVector: Float32Array, newCreatedAt: number, newLogicalKey: string | undefined, newMetadata: string | null | undefined, members: readonly CandidateMember[], bands: {
91
+ readonly nearDuplicate: number;
92
+ readonly conflictLow: number;
93
+ }) => readonly EdgeCandidate[];
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Inductive concept clustering — k-means over memory embeddings, plus the
3
+ * `concept.cluster` job handler that materializes concepts + annotations.
4
+ *
5
+ * ## The clustering pipeline
6
+ *
7
+ * The pure core is a deterministic k-means (>2 groups) over L2-normalized
8
+ * vectors, built from the same primitives as the topic-tree splitter
9
+ * (`topics/tree.ts` — never modified):
10
+ *
11
+ * 1. `farthestPointSeeds` — farthest-first seeding: the first two seeds
12
+ * are the farthest pair, each next seed is the vector farthest from
13
+ * its nearest existing seed.
14
+ * 2. `kGroupLloyd` — nearest-centroid assignment + centroid recomputation,
15
+ * iterated for a fixed number of passes or until a fixpoint.
16
+ * 3. `clusterMembers` — the full pipeline on the content-canonical order
17
+ * (see `canonicalOrder`), so the result is a function of the vector
18
+ * SET, never of the caller's array order. Group assignments are
19
+ * un-permuted back to the caller's index space.
20
+ *
21
+ * A surviving cluster (≥ `clusterMinSize` members) becomes one concept row:
22
+ * content-addressed id `concept_<fnv1a32 of the centroid's floats>`, null
23
+ * name (inductive clusters are anonymous — the name column is nullable), the
24
+ * centroid as embedding, and one `memory_concepts` row per member with
25
+ * confidence = cosine similarity to the centroid.
26
+ *
27
+ * ## Determinism
28
+ *
29
+ * Same DB state + same config ⇒ identical clusters: canonical ordering,
30
+ * index tie-breaks (smaller wins) everywhere, float32 accumulation in
31
+ * canonical order, ids hashed from centroid floats only. Audit timestamps
32
+ * come from the injectable Effect Clock (observability only).
33
+ *
34
+ * ## Dimension assumption
35
+ *
36
+ * The prototype assumes the ACTIVE model is 384-dimensional: the
37
+ * `concepts.embedding` column is a fixed `F32_BLOB(384)`, so centroids from
38
+ * any other dimension cannot be stored. Cluster jobs are only safe while
39
+ * the active model stays 384-dim.
40
+ */
41
+ import { Effect } from "effect";
42
+ import { JobFailure, type Handler, type JobContext } from "./internal/workflow/index.js";
43
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
44
+ import { Service as DbBackend } from "../db-backend.ts";
45
+ import type { Interface as DbBackendInterface } from "../db-backend.ts";
46
+ import { StoreError } from "../error.ts";
47
+ import type { GraphConfig } from "../schema/graph.ts";
48
+ /**
49
+ * Deterministic farthest-first seeding for k groups.
50
+ *
51
+ * The first two seeds are `farthestPair`'s result (its contract: `a = 0` is
52
+ * the first vector — callers must pass the CONTENT-CANONICAL order, exactly
53
+ * as `splitPlan` does). Each next seed is the unchosen vector whose minimum
54
+ * dot product against the existing seeds is smallest — the vector farthest
55
+ * from its nearest seed under the normalized-vector invariant. Ties break to
56
+ * the smaller index; already-chosen indices are never re-picked (a seed is
57
+ * distance 0 from itself, so without the exclusion the tie-break could
58
+ * return a seed).
59
+ *
60
+ * `k` clamps to the vector count (an empty input yields `[]`); fewer than 2
61
+ * vectors yield the degenerate `[0]` / `[a, b]` forms of `farthestPair`.
62
+ * Never mutates inputs.
63
+ */
64
+ export declare const farthestPointSeeds: (vectors: readonly Float32Array[], k: number) => readonly number[];
65
+ /**
66
+ * Deterministic k-group Lloyd refinement: assign every vector to the group
67
+ * with the highest dot product against the group's centroid (initial seeds,
68
+ * then recomputed means), iterating for `passes` iterations. Ties go to the
69
+ * smaller GROUP index (position in `seeds`). Stops early at a fixpoint —
70
+ * further passes are no-ops, so the result is identical either way.
71
+ *
72
+ * Centroid math follows `tree.ts`'s `lloydRefine`: group CF-triplets via
73
+ * `cfFromVectors`, float64 `dot(x, ls) / count` scoring for reassignment,
74
+ * float32 `centroidOf` for the reported centroids. Empty groups score 0 and
75
+ * stay empty (their centroid is the zero vector).
76
+ *
77
+ * Never mutates inputs. `seeds` holds indices into `vectors` (the seed
78
+ * vectors themselves — the initial assignment is nearest-seed, not
79
+ * seed-region).
80
+ */
81
+ export declare const kGroupLloyd: (vectors: readonly Float32Array[], seeds: readonly number[], passes: number) => {
82
+ groups: Uint32Array;
83
+ centroids: readonly Float32Array[];
84
+ };
85
+ /** Result of {@link clusterMembers}: per-vector group assignment + centroids. */
86
+ export interface ClusterResult {
87
+ /** `groups[i]` = the group of `vectors[i]` (caller index space). */
88
+ readonly groups: Uint32Array;
89
+ /** One centroid per group, in group-index order. */
90
+ readonly centroids: readonly Float32Array[];
91
+ }
92
+ /**
93
+ * Deterministic k-means over one vector set. Runs the whole pipeline on the
94
+ * content-canonical order (mirroring `splitPlan`'s v3 determinism): canonical
95
+ * permutation → farthest-first seeds → k-group Lloyd → un-permute the
96
+ * assignment back to the caller's index space. `k` clamps to the vector
97
+ * count. The result is a function of the vector SET — never of the caller's
98
+ * array order. Never mutates inputs.
99
+ */
100
+ export declare const clusterMembers: (vectors: readonly Float32Array[], k: number, passes: number) => ClusterResult;
101
+ /**
102
+ * Content-addressed concept id: `concept_` + FNV-1a 32-bit hex over the
103
+ * JSON of the centroid's float32 values. The centroid is computed on the
104
+ * canonical order, so identical embeddings yield identical ids across runs
105
+ * and DBs (a hash collision merges two clusters into one concept row — the
106
+ * same 32-bit collision odds as the topic child keys).
107
+ */
108
+ export declare const clusterConceptId: (centroid: Float32Array) => string;
109
+ /**
110
+ * Cosine similarity of a member vector to its cluster centroid. Vectors are
111
+ * L2-normalized (the tree.ts invariant), so the cosine is
112
+ * `dot(x, c) / |c|`; a zero centroid yields 0. Clamped to [0, 1] — a
113
+ * negative cosine means "opposite direction", not a confident membership.
114
+ */
115
+ export declare const conceptConfidence: (vector: Float32Array, centroid: Float32Array) => number;
116
+ /** Result of one cluster pass: how many concepts + annotations were written. */
117
+ export interface ClusterWriteResult {
118
+ /** Concepts upserted (clusters that met `clusterMinSize`). */
119
+ readonly concepts: number;
120
+ /** `memory_concepts` rows inserted/refreshed. */
121
+ readonly annotated: number;
122
+ }
123
+ /**
124
+ * Cluster ONE topic leaf's members into concepts: load the model-scoped
125
+ * memberships, fetch the members' vectors from the model's per-model vector
126
+ * table, cluster, and write the surviving clusters. A topic with no
127
+ * memberships (or no vector rows) writes nothing — the idempotent no-op
128
+ * that makes stale job re-offers harmless.
129
+ */
130
+ export declare const clusterLeaf: (topicKey: string, model: EmbeddingModelConfig, config: GraphConfig) => Effect.Effect<ClusterWriteResult, StoreError, DbBackend>;
131
+ /**
132
+ * The CORPUS pass: cluster a deterministic sample of the memory store and
133
+ * annotate the sampled memories. The sample is every Nth memory ordered by
134
+ * `memory_id` (N chosen to reach ~5000 samples; N = 1 when the store is
135
+ * smaller), active memories with an embedding only. Embeddings come from the
136
+ * `memories.embedding` column — the ACTIVE model's embeddings, written by
137
+ * the write hook (see the module doc's dimension assumption).
138
+ */
139
+ export declare const clusterCorpus: (model: EmbeddingModelConfig, config: GraphConfig) => Effect.Effect<ClusterWriteResult, StoreError, DbBackend>;
140
+ /** Dependencies for {@link conceptClusterHandler}. */
141
+ export interface ConceptClusterHandlerDeps {
142
+ /** Resolves a model key to its config (the model registry). */
143
+ readonly getModelConfig: (modelKey: string) => EmbeddingModelConfig | undefined;
144
+ /** Graph config — `clusterK` / `clusterMinSize` drive the clustering. */
145
+ readonly config: GraphConfig;
146
+ /** Backend captured at registration (see {@link topicSplitHandler}). */
147
+ readonly backend?: DbBackendInterface;
148
+ }
149
+ /** The `concept.cluster` handler shape (see jobs.ts). */
150
+ export type ConceptClusterHandler = (payload: unknown, ctx: JobContext) => Effect.Effect<unknown, JobFailure, DbBackend>;
151
+ /**
152
+ * Factory for the `concept.cluster` job handler. Two modes, selected by the
153
+ * payload:
154
+ *
155
+ * - `{ modelKey, topicKey }` — cluster ONE topic leaf's members (per-tree
156
+ * inductive pass).
157
+ * - `{ modelKey }` — the CORPUS pass: cluster a deterministic sample of the
158
+ * memory store.
159
+ *
160
+ * The success schema is `Schema.Void` — the outcome lives in the DB
161
+ * (concepts + `memory_concepts` rows), not the job result. A pass that
162
+ * writes nothing (no members, no clusters meeting `clusterMinSize`) is a
163
+ * success, never an error — the idempotent no-op that makes stale re-offers
164
+ * harmless.
165
+ */
166
+ export declare function conceptClusterHandler(deps: ConceptClusterHandlerDeps & {
167
+ readonly backend: DbBackendInterface;
168
+ }): Handler;
169
+ export declare function conceptClusterHandler(deps: ConceptClusterHandlerDeps): ConceptClusterHandler;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * The consolidation dream job — `memory.dream`. One run per model key:
3
+ * gather candidate memory pairs (rule-derived pending edges + a fresh
4
+ * near-duplicate scan over memories created since the watermark), ask the
5
+ * injected LLM classifier to judge them, then persist its relations and
6
+ * derived memories.
7
+ *
8
+ * The store never imports an AI SDK: {@link DreamClassifier} is the injected
9
+ * contract — the app root composes the real implementation from the llm
10
+ * package. The handler fails the job on unknown-model, persistence, AND
11
+ * classifier errors (a silent "success" would hide a classifier outage
12
+ * from the caller; the watermark never advances past an unjudged batch).
13
+ *
14
+ * ## Run flow
15
+ *
16
+ * The run proceeds in stages. It first loads the model's `dream_state`
17
+ * row (the watermark) and counts memories created since it; the gates
18
+ * (`minHours` elapsed since the last dream AND `minNewMemories` new
19
+ * memories) must both pass, else the run returns early with
20
+ * `skipped: true` and a reason. Next it gathers candidate pairs from two
21
+ * sources, deduplicated and bounded by `maxPairs` in deterministic order:
22
+ * pending `artifact_edges` rows written by the write-path rules (status
23
+ * `pending`, source `rule`), and a fresh near-duplicate scan
24
+ * (`leafLocalCandidates`) of each new memory against its topic-leaf
25
+ * mates. Both memories' contents are loaded for every pair. Then it calls
26
+ * `classify(pairs, { maxDerived })` — any classifier error FAILS the job
27
+ * (the watermark stays untouched, so the retried run judges the batch
28
+ * again). Finally it persists
29
+ * relations and derived memories (see below) and advances the watermark
30
+ * in ONE upsert, only after every write succeeded. A persistence error
31
+ * propagates as a job failure — the watermark stays put, so the retried
32
+ * run redoes the writes, which are all check-first idempotent.
33
+ *
34
+ * ## Persistence rules
35
+ *
36
+ * - Relations: an edge is skipped when either endpoint memory is pinned
37
+ * (`MemoryMetadata.pinned`) or no longer exists. `memory_supersedes`
38
+ * with confidence ≥ 0.8 is written CONFIRMED and flips the older
39
+ * (`from`) memory to `superseded` in the same transaction (the shared
40
+ * active→superseded flip + `superseded` event, mirroring the edge API);
41
+ * everything else (`memory_supersedes` below the bar,
42
+ * `memory_contradicts`, `memory_supports`) stays pending. All dream edges
43
+ * carry source `llm`. Source memories are NEVER modified except that
44
+ * supersede flip.
45
+ * - Derived memories: provenance enforcement — at least one declared source
46
+ * must still exist, else the derived memory is skipped. The memory is
47
+ * written content-addressed (`derived:<fnv1a32(content)>`, active-key
48
+ * dedupe makes re-derivation idempotent), its metadata blob records the
49
+ * surviving source ids, and one pending `memory_supports` edge is written
50
+ * from each surviving source.
51
+ *
52
+ * ## Determinism
53
+ *
54
+ * Every iteration derives from persisted state: pending edges by
55
+ * `created_at` then `id`, new memories by `memory_id`, the combined pair
56
+ * set sorted lexicographically, relations/derived processed in the
57
+ * classifier's output order. Audit timestamps come from the injectable
58
+ * Effect Clock (observability only).
59
+ */
60
+ import { Effect } from "effect";
61
+ import { JobFailure, type Handler, type JobContext } from "./internal/workflow/index.js";
62
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
63
+ import { Service as DbBackend } from "../db-backend.ts";
64
+ import type { Interface as DbBackendInterface } from "../db-backend.ts";
65
+ /** One memory pair judged by the classifier (older first — the direction
66
+ * the graph edge API expects: the older memory is the supersede target). */
67
+ export interface DreamPair {
68
+ readonly olderId: string;
69
+ readonly newerId: string;
70
+ readonly olderContent: string;
71
+ readonly newerContent: string;
72
+ }
73
+ /** One judged relation between two existing memories. */
74
+ export interface DreamRelation {
75
+ readonly fromMemoryId: string;
76
+ readonly toMemoryId: string;
77
+ readonly edgeKind: "memory_supersedes" | "memory_contradicts" | "memory_supports";
78
+ readonly confidence: number;
79
+ }
80
+ /** One new memory to synthesize from the judged sources. */
81
+ export interface DreamDerived {
82
+ readonly content: string;
83
+ readonly sources: readonly string[];
84
+ }
85
+ /** The classifier's judgement of one pair batch. */
86
+ export interface DreamClassification {
87
+ readonly relations: readonly DreamRelation[];
88
+ readonly derived: readonly DreamDerived[];
89
+ }
90
+ /**
91
+ * The injected classifier contract. The store never imports an AI SDK —
92
+ * the app root provides an implementation that calls the model with the
93
+ * pair contents and returns relations + derived memories. Errors are
94
+ * swallowed by the handler (logged, watermark untouched); the channel is
95
+ * `unknown` precisely because the store cannot name the implementation's
96
+ * error type.
97
+ */
98
+ export interface DreamClassifier {
99
+ readonly classify: (pairs: readonly DreamPair[], opts: {
100
+ readonly maxDerived: number;
101
+ }) => Effect.Effect<DreamClassification, unknown>;
102
+ }
103
+ /**
104
+ * Dream settings used when the graph config carries none (the config schema
105
+ * has no dream fields — these local defaults are the source of truth):
106
+ * at least `minHours` between dream runs, at least `minNewMemories` new
107
+ * memories to bother the classifier, at most `maxPairs` judged per run, and
108
+ * at most `maxDerived` synthesized memories per run.
109
+ */
110
+ export declare const dreamStateDefaults: {
111
+ readonly minHours: 24;
112
+ readonly minNewMemories: 20;
113
+ readonly maxPairs: 200;
114
+ readonly maxDerived: 10;
115
+ };
116
+ /** Outcome of one dream run. The handler returns this value even though the
117
+ * job's success schema is void — the result is for direct callers/tests;
118
+ * the persisted job result stays void (the watermark row is the durable
119
+ * state). */
120
+ export interface DreamOutcome {
121
+ /** True when the gates rejected the run before any work. */
122
+ readonly skipped: boolean;
123
+ /** Skip explanation (gates). */
124
+ readonly reason?: string;
125
+ /** Pairs sent to the classifier. */
126
+ readonly classified: number;
127
+ /** Derived memories actually inserted. */
128
+ readonly created: number;
129
+ /** Memories flipped active → superseded by confirmed supersede edges. */
130
+ readonly superseded: number;
131
+ }
132
+ /** Dependencies for {@link graphDreamHandler}. */
133
+ export interface DreamHandlerDeps {
134
+ /** Resolves a model key to its config (the model registry). */
135
+ readonly getModelConfig: (modelKey: string) => EmbeddingModelConfig | undefined;
136
+ /**
137
+ * The injected classifier that judges candidate pairs. The app root
138
+ * implements it over any LLM (for example `makeDreamClassifier` from
139
+ * `@dimple/llm`); the store never imports an AI SDK. The classifier
140
+ * receives the gathered pairs plus a `maxDerived` budget and must return
141
+ * relations (canonical direction: supersede/contradict edges point
142
+ * old → new) and optional derived memories with provenance sources.
143
+ */
144
+ readonly classify: DreamClassifier;
145
+ /** Backend captured at registration (see {@link topicSplitHandler}). */
146
+ readonly backend?: DbBackendInterface;
147
+ }
148
+ /** The `memory.dream` handler shape (see jobs.ts). */
149
+ export type DreamHandler = (payload: unknown, ctx: JobContext) => Effect.Effect<unknown, JobFailure, DbBackend>;
150
+ /**
151
+ * Factory for the `memory.dream` job handler. Payload: `{ modelKey }`.
152
+ * Runs the four phases (orient → gather → consolidate → prune) for that
153
+ * model's tree, then advances the model's `dream_state` watermark in ONE
154
+ * upsert — only after every write succeeded.
155
+ *
156
+ * Gates (from `dreamStateDefaults`): the run skips unless at least
157
+ * `minHours` passed since the last dream AND at least `minNewMemories`
158
+ * memories were created since the watermark. A fresh model (no state row)
159
+ * passes both gates trivially.
160
+ *
161
+ * The success schema is `Schema.Void` — the durable outcome lives in the
162
+ * `dream_state` row + the written edges/memories; the returned
163
+ * {@link DreamOutcome} is for direct callers. Skipped and
164
+ * classifier-failed runs are successes, never errors (a skipped run is
165
+ * normal cadence; a failed classifier must not crash the worker or retry
166
+ * forever — the watermark stays put so the batch is judged again next run).
167
+ */
168
+ export declare function graphDreamHandler(deps: DreamHandlerDeps & {
169
+ readonly backend: DbBackendInterface;
170
+ }): Handler;
171
+ export declare function graphDreamHandler(deps: DreamHandlerDeps): DreamHandler;
@@ -0,0 +1,25 @@
1
+ import { Schema } from "effect";
2
+ declare const EdgeNotFound_base: Schema.Class<EdgeNotFound, Schema.TaggedStruct<"graph.EdgeNotFound", {
3
+ readonly id: Schema.String;
4
+ }>, import("effect/Cause").YieldableError>;
5
+ /**
6
+ * Graph error types (mirror of `topics/errors.ts`). Each distinct failure
7
+ * mode gets its own `Schema.TaggedErrorClass` so callers can `catchTag`
8
+ * precisely; they ride the public `Store` error channel alongside
9
+ * `StoreError`.
10
+ */
11
+ /** A status/soft-delete transition targeted an edge id that does not exist. */
12
+ export declare class EdgeNotFound extends EdgeNotFound_base {
13
+ }
14
+ declare const EdgeExists_base: Schema.Class<EdgeExists, Schema.TaggedStruct<"graph.EdgeExists", {
15
+ readonly fromMemoryId: Schema.String;
16
+ readonly toMemoryId: Schema.String;
17
+ readonly edgeKind: Schema.String;
18
+ }>, import("effect/Cause").YieldableError>;
19
+ /**
20
+ * `Store.addEdge` hit the `(from_memory_id, to_memory_id, edge_kind)`
21
+ * unique constraint (the composite PK) — the edge already exists.
22
+ */
23
+ export declare class EdgeExists extends EdgeExists_base {
24
+ }
25
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Graph barrel. The artifact graph's store-level surface: canonical errors
3
+ * (`EdgeNotFound` / `EdgeExists`), the traversal row schema + the sanctioned
4
+ * boundary cast (`fromNeighborRows`), and the graph schema (`artifact_edges`
5
+ * table, the typed `graph_edges` view handle, `GraphConfig`).
6
+ */
7
+ export * from "./errors.ts";
8
+ export * from "./traversal.ts";
9
+ export * from "./retrieval-post-pass.ts";
10
+ export * from "./concepts.ts";
11
+ export * from "./dream.ts";
12
+ export * from "./candidates.ts";
13
+ export * from "./observability.ts";
14
+ export * from "./repair.ts";
15
+ export * from "../schema/graph.ts";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Graph coverage — the observability snapshot of the artifact graph's
3
+ * reach over one model's memory population.
4
+ *
5
+ * The memory counts are scoped to the model: "the model's memories" are
6
+ * the active `memories` rows whose `embedding_model` column names the
7
+ * model (set by `Store.updateEmbedding` — the moment a memory joins a
8
+ * model's vector space). Edges and concepts are global tables, so the
9
+ * incident-edge survey and the pending-candidate count are computed over
10
+ * the whole graph, but the denominators (total / covered / isolated) are
11
+ * model-scoped.
12
+ *
13
+ * All reads are plain Drizzle builder queries (one id-column scan per
14
+ * count) plus a single `count(*)` aggregate for the pending-candidate
15
+ * backlog — no raw SQL, no backend methods.
16
+ */
17
+ import { Effect } from "effect";
18
+ import { Service as DbBackend } from "../db-backend.ts";
19
+ import { StoreError } from "../error.ts";
20
+ /** One coverage snapshot for a model's memory population. */
21
+ export interface GraphCoverage {
22
+ /** Active memories embedded by the model. */
23
+ readonly totalMemories: number;
24
+ /** Of those, with at least one live incident edge (confirmed or pending, not expired). */
25
+ readonly coveredMemories: number;
26
+ /** `coveredMemories / totalMemories` as a percentage, rounded to one decimal (0 when empty). */
27
+ readonly coveragePercent: number;
28
+ /** Pending, non-expired `artifact_edges` rows — the write-path candidate backlog. */
29
+ readonly pendingCandidates: number;
30
+ /** Total concept rows (concepts are global, not per-model). */
31
+ readonly conceptCount: number;
32
+ /** Distinct memories with at least one concept annotation. */
33
+ readonly annotatedMemories: number;
34
+ /** Model memories with no live incident edge (the `graphCandidates` survey mirror). */
35
+ readonly isolatedMemories: number;
36
+ }
37
+ export declare const graphCoverage: (modelKey: string) => Effect.Effect<GraphCoverage, StoreError, DbBackend>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The graph repair job — `graph.repair`. One pass per model key that
3
+ * sweeps the graph tables for rows that should not exist and removes them.
4
+ *
5
+ * The scan is global (edges, concepts and annotations are not per-model
6
+ * tables); the payload's model key is validated against the model registry
7
+ * so the job's idempotency token (`repair:<modelKey>`) always refers to a
8
+ * real model, exactly like the other graph jobs.
9
+ *
10
+ * ## What the pass fixes
11
+ *
12
+ * 1. ORPHANED EDGES — `artifact_edges` rows whose `from` or `to` memory
13
+ * row is gone. Normally impossible (the schema declares cascade
14
+ * deletes), but a crash window or a foreign-key-disabled database can
15
+ * leave them behind; they are dead weight for traversal and the
16
+ * candidates review. Detected with a double left join against
17
+ * `memories`; deleted outright.
18
+ * 2. STALE CONCEPT ANNOTATIONS — `memory_concepts` rows whose concept row
19
+ * or memory row is gone (the same crash-window class). Deleted by
20
+ * their composite key.
21
+ * 3. EXPIRED EDGES (OPTIONAL) — soft-deleted edges (`expired_at` set)
22
+ * older than a grace window. The soft-delete keeps history around for
23
+ * review; the purge makes it a hard delete. Gated by the handler
24
+ * option `includePurgeExpired` (default false) — the conservative
25
+ * default keeps the pass read-only on edge history.
26
+ *
27
+ * ## Logging and idempotence
28
+ *
29
+ * Every fix is recorded as a `memory_events` row with event type
30
+ * `graph_repair` and a JSON `reason` describing the action — attached to
31
+ * a surviving endpoint memory where one exists. An orphan whose BOTH
32
+ * endpoints are gone has no surviving memory to attach an event to (the
33
+ * events table's memory id is a foreign key to `memories`), so those
34
+ * deletions are the one case that is not logged — the row itself was the
35
+ * only trace. All reads and writes run in ONE transaction, so a
36
+ * mid-pass failure rolls back every fix and the counts; a re-run then
37
+ * finds the same state and repeats cleanly (idempotent — the second run
38
+ * simply finds nothing to fix).
39
+ */
40
+ import { Effect } from "effect";
41
+ import { JobFailure, type Handler, type JobContext } from "./internal/workflow/index.js";
42
+ import type { EmbeddingModelConfig } from "./internal/config/index.js";
43
+ import { Service as DbBackend } from "../db-backend.ts";
44
+ import type { Interface as DbBackendInterface } from "../db-backend.ts";
45
+ /** How many rows of each class one repair pass fixed. */
46
+ export interface RepairResult {
47
+ /** `artifact_edges` rows whose endpoint memory was gone. */
48
+ readonly orphanedEdges: number;
49
+ /** `memory_concepts` rows whose concept or memory was gone. */
50
+ readonly staleAnnotations: number;
51
+ /** Soft-deleted edges hard-purged past the grace window (only when enabled). */
52
+ readonly expiredEdges: number;
53
+ }
54
+ /** Dependencies for {@link graphRepairHandler}. */
55
+ export interface GraphRepairHandlerDeps {
56
+ /** Resolves a model key to its config (the model registry). */
57
+ readonly getModelConfig: (modelKey: string) => EmbeddingModelConfig | undefined;
58
+ /** Backend captured at registration (see {@link topicSplitHandler}). */
59
+ readonly backend?: DbBackendInterface;
60
+ /** When true, hard-purge soft-deleted edges older than `expiredGraceMs`.
61
+ * Default false — the expired sweep is skipped entirely. */
62
+ readonly includePurgeExpired?: boolean;
63
+ /** Grace window for the expired purge in ms (default one week). */
64
+ readonly expiredGraceMs?: number;
65
+ }
66
+ /** The `graph.repair` handler shape (see jobs.ts). */
67
+ export type GraphRepairHandler = (payload: unknown, ctx: JobContext) => Effect.Effect<unknown, JobFailure, DbBackend>;
68
+ /**
69
+ * Factory for the `graph.repair` job handler. Payload: `{ modelKey }`.
70
+ * Validates the model key, then runs the repair transaction: orphaned
71
+ * `artifact_edges` (endpoints missing — normally impossible via FK
72
+ * cascade, the backstop for crash windows), stale `memory_concepts`
73
+ * annotations, and — only when `includePurgeExpired` is set — hard-purge
74
+ * of soft-deleted edges older than `expiredGraceMs`. Every fix is logged
75
+ * to `memory_events` (`event_type = "graph_repair"`). Idempotent: a
76
+ * second pass finds nothing and writes nothing.
77
+ *
78
+ * The success schema is `Schema.Void` — the durable outcome is the
79
+ * deleted rows + the `graph_repair` event log; the returned
80
+ * {@link RepairResult} is for direct callers/tests.
81
+ */
82
+ export declare function graphRepairHandler(deps: GraphRepairHandlerDeps & {
83
+ readonly backend: DbBackendInterface;
84
+ }): Handler;
85
+ export declare function graphRepairHandler(deps: GraphRepairHandlerDeps): GraphRepairHandler;