@ponraaj/dimple-sdk 0.0.0-alpha.3 → 0.0.0-alpha.5
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.
- package/README.md +233 -11
- package/dist/dimple.d.ts +101 -3
- package/dist/errors.d.ts +23 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1008 -241
- package/dist/indexer.d.ts +9 -0
- package/dist/internal/config/schema.d.ts +27 -0
- package/dist/internal/core/hosted-embedding.d.ts +33 -0
- package/dist/internal/embedder/auto-install.d.ts +35 -0
- package/dist/internal/embedder/index.d.ts +0 -1
- package/dist/internal/embedder/transformers.d.ts +10 -1
- package/dist/internal/extractor/models/extraction.d.ts +14 -0
- package/dist/internal/logger/index.d.ts +1 -1
- package/dist/internal/logger/service.d.ts +15 -7
- package/dist/internal/store/db-backend.d.ts +14 -2
- package/dist/internal/store/graph/candidates.d.ts +3 -1
- package/dist/internal/store/models.d.ts +4 -3
- package/dist/internal/store/schema/graph.d.ts +7 -7
- package/dist/internal/store/schema/libsql.d.ts +4 -4
- package/dist/internal/store/schema/metadata.d.ts +4 -1
- package/dist/internal/store/service.d.ts +45 -1
- package/dist/make.d.ts +21 -0
- package/package.json +12 -14
- package/dist/internal/embedder/openai.d.ts +0 -8
package/README.md
CHANGED
|
@@ -12,6 +12,25 @@ thin facade over it.
|
|
|
12
12
|
|
|
13
13
|
> **Status**: alpha (`0.0.0-alpha.n`). The API is stabilizing but may change.
|
|
14
14
|
|
|
15
|
+
### The one-liner
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { makeDimple } from "@ponraaj/dimple-sdk"; // ← makeDimple, not dimple
|
|
19
|
+
|
|
20
|
+
const dimple = await makeDimple({ config: "./dimple.jsonc" });
|
|
21
|
+
const hits = await dimple.query("what did we decide about X", {
|
|
22
|
+
limit: 5,
|
|
23
|
+
contentType: "text/plain", // ← code-vs-message separation
|
|
24
|
+
}); // query(text, options) — text is the FIRST positional argument
|
|
25
|
+
await dimple.dispose();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Calling conventions: every method takes **plain arguments** (`query(text,
|
|
29
|
+
opts)`, `write(input, opts)`, `index({ paths })`), every failure is a
|
|
30
|
+
rejected promise with a closed `_tag` (`"Sdk.NotFound"`, `"Sdk.InputError"`,
|
|
31
|
+
…), and `dispose()` is **required** before `process.exit(0)` — the durable
|
|
32
|
+
worker keeps the event loop alive (`dispose()` stops it; it is idempotent).
|
|
33
|
+
|
|
15
34
|
## Install
|
|
16
35
|
|
|
17
36
|
```bash
|
|
@@ -19,8 +38,10 @@ npm install @ponraaj/dimple-sdk
|
|
|
19
38
|
```
|
|
20
39
|
|
|
21
40
|
Runtime dependencies are the only things installed (Effect, libSQL,
|
|
22
|
-
Transformers.js, drizzle) — the workspace internals are
|
|
23
|
-
package
|
|
41
|
+
Transformers.js, drizzle) — the workspace internals (`@dimple/*`) are
|
|
42
|
+
bundled into the package, including fully **self-contained type
|
|
43
|
+
declarations** (the publish build vendors every internal d.ts and rewrites
|
|
44
|
+
the `@dimple/*` specifiers — plain consumers never need the workspace).
|
|
24
45
|
|
|
25
46
|
## Quickstart
|
|
26
47
|
|
|
@@ -36,7 +57,9 @@ const mem = await dimple.write({
|
|
|
36
57
|
});
|
|
37
58
|
|
|
38
59
|
// hybrid semantic + FTS search
|
|
39
|
-
const results = await dimple.query("how does the pipeline deploy", {
|
|
60
|
+
const results = await dimple.query("how does the pipeline deploy", {
|
|
61
|
+
limit: 3,
|
|
62
|
+
});
|
|
40
63
|
|
|
41
64
|
// index real code — files + extracted functions/classes/methods become memories
|
|
42
65
|
const indexed = await dimple.index({ paths: ["./src"] });
|
|
@@ -80,7 +103,7 @@ Pass a config file path or an inline object:
|
|
|
80
103
|
|
|
81
104
|
```ts
|
|
82
105
|
const dimple = await makeDimple({ config: "./dimple.jsonc" });
|
|
83
|
-
// or
|
|
106
|
+
// or — the inline object mirrors the JSON config exactly (quoted keys):
|
|
84
107
|
const dimple = await makeDimple({
|
|
85
108
|
config: {
|
|
86
109
|
storeUrl: "file:./.dimple/dimple.db",
|
|
@@ -114,10 +137,58 @@ const dimple = await makeDimple({
|
|
|
114
137
|
| `defaultK` / `defaultLimit` | numbers | retrieval defaults |
|
|
115
138
|
| `defaultTtlSecs` | number | write TTL default |
|
|
116
139
|
|
|
140
|
+
### Remote databases (Turso)
|
|
141
|
+
|
|
142
|
+
`storeUrl` and `jobs.dbUrl` accept **remote libSQL URLs** (`libsql://`,
|
|
143
|
+
`https://`, `ws://` — Turso or any libsql-compatible host). The same
|
|
144
|
+
engine runs everything the local file does — FTS5, vector search, the
|
|
145
|
+
topic tree, durable jobs — over the wire. Verified end-to-end against
|
|
146
|
+
the real Turso service (`tests/store/turso-remote.test.ts`).
|
|
147
|
+
|
|
148
|
+
Turso tokens are **per-database**, so each URL carries its own token.
|
|
149
|
+
`dimple.jsonc` resolves `${ENV_VAR}` in values, so tokens stay out of
|
|
150
|
+
config files:
|
|
151
|
+
|
|
152
|
+
```jsonc
|
|
153
|
+
{
|
|
154
|
+
"storeUrl": "libsql://<org>-<memdb>.turso.io?authToken=${TURSO_MEMORIES_TOKEN}",
|
|
155
|
+
"jobs": {
|
|
156
|
+
"dbUrl": "libsql://<org>-<jobsdb>.turso.io?authToken=${TURSO_JOBS_TOKEN}",
|
|
157
|
+
},
|
|
158
|
+
"embedding": {
|
|
159
|
+
/* hosted providers for serverless — local models are
|
|
160
|
+
local-only (auto-install needs Node) */
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Notes:
|
|
166
|
+
|
|
167
|
+
- The **jobs DB must also be remote** when the store is remote (the
|
|
168
|
+
durable queue + workflow state are cross-invocation)
|
|
169
|
+
- `file:`-only behaviors (parent-dir creation, migration lock) skip
|
|
170
|
+
for remote URLs
|
|
171
|
+
- A single-DB setup can share one URL + one token (`TURSO_TOKEN`)
|
|
172
|
+
- For Cloudflare Workers the client swaps to the pure-JS entry
|
|
173
|
+
(`@libsql/client/web` — one bundler alias, no source changes)
|
|
174
|
+
|
|
117
175
|
### Embedding models
|
|
118
176
|
|
|
119
|
-
|
|
120
|
-
|
|
177
|
+
Local embeddings (`provider: "transformers"`) auto-install on first
|
|
178
|
+
use — `@huggingface/transformers` is an **optional peer** so hosted-only
|
|
179
|
+
consumers never pull its tree (the `sharp` + `onnxruntime-node`
|
|
180
|
+
postinstall scripts that abort npm 11 installs). The first `embed()`
|
|
181
|
+
installs it with the project's own package manager, scripts skipped
|
|
182
|
+
(proven unnecessary for embedding), and retries in-process. Offline or
|
|
183
|
+
restricted environments get a typed `Sdk.Embedding` error with the manual
|
|
184
|
+
command. Set `autoInstall: false` on the model to disable:
|
|
185
|
+
`embedding.models[].autoInstall: false`
|
|
186
|
+
|
|
187
|
+
### Embedding models
|
|
188
|
+
|
|
189
|
+
Two providers — **local** (Transformers.js ONNX, runs on your CPU, no
|
|
190
|
+
keys) and **hosted** (any OpenAI-compatible endpoint, plus native
|
|
191
|
+
provider families):
|
|
121
192
|
|
|
122
193
|
```jsonc
|
|
123
194
|
"embedding": {
|
|
@@ -128,11 +199,32 @@ provider):
|
|
|
128
199
|
{ "id": "bge", "provider": "transformers",
|
|
129
200
|
"model": "Xenova/bge-small-en-v1.5", "dimensions": 384 },
|
|
130
201
|
{ "id": "bge-base", "provider": "transformers",
|
|
131
|
-
"model": "Xenova/bge-base-en-v1.5", "dimensions": 768 }
|
|
202
|
+
"model": "Xenova/bge-base-en-v1.5", "dimensions": 768 },
|
|
203
|
+
{ "id": "gemini", "provider": "openai",
|
|
204
|
+
"model": "gemini-embedding-2", "dimensions": 3072, "apiKey": "…",
|
|
205
|
+
"baseURL": "https://generativelanguage.googleapis.com/v1beta/openai/" },
|
|
206
|
+
{ "id": "openai", "provider": "openai",
|
|
207
|
+
"model": "text-embedding-3-small", "dimensions": 1536, "apiKey": "…" }
|
|
132
208
|
]
|
|
133
209
|
}
|
|
134
210
|
```
|
|
135
211
|
|
|
212
|
+
**Hosted providers** — the config `provider` field selects the family:
|
|
213
|
+
|
|
214
|
+
| `provider` | Models | `baseURL` needed? |
|
|
215
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
216
|
+
| `openai` (default) | any OpenAI-compatible endpoint — OpenAI, vLLM, **ollama** (`http://localhost:11434/v1`), Ollama Cloud, LM Studio, Together, Fireworks, Groq, Mistral, … | optional (defaults to OpenAI) — set it for custom endpoints |
|
|
217
|
+
| `google` | `gemini-embedding-001`, `gemini-embedding-2`, `gemini-embedding-2-preview` (native provider) | ❌ no |
|
|
218
|
+
| `cohere` | `embed-english-v3.0`, `embed-multilingual-v3.0`, … (native provider) | ❌ no |
|
|
219
|
+
| `bedrock` | `amazon.titan-embed-text-v2:0`, `amazon.nova-embed-text-v2:0`, `cohere.embed-*` (native provider; AWS creds from env) | ❌ no |
|
|
220
|
+
|
|
221
|
+
`dimensions` is sent as a provider option where the API supports it
|
|
222
|
+
(openai-compatible/`outputDimensionality`/`outputDimension`/titan `dimensions`
|
|
223
|
+
and nova `embeddingDimension`) and ALWAYS enforced on the output.
|
|
224
|
+
Verified end-to-end against real `gemini-embedding-2` (3072-dim), local
|
|
225
|
+
ollama `Qwen3-Embedding-0.6B` (1024-dim), and the openai-compatible
|
|
226
|
+
contract.
|
|
227
|
+
|
|
136
228
|
- `dimensions` must match the model's real output — a mismatch fails typed
|
|
137
229
|
on first use
|
|
138
230
|
- Multiple models coexist: each gets its own vector tables
|
|
@@ -199,6 +291,11 @@ active `logicalKey` → `Sdk.DuplicateKey` (carries `logicalKey`/`namespace`).
|
|
|
199
291
|
**Memory expiry**: `ttlSecs` (or the config `defaultTtlSecs`) schedules
|
|
200
292
|
expiry; a sweep job retires expired memories in the background.
|
|
201
293
|
|
|
294
|
+
`query(text, { budget: { maxUnits, maxChars, maxMs } })` — LLM-facing
|
|
295
|
+
context guardrail: caps the row count, truncates result content to the
|
|
296
|
+
total char budget, and marks `results.truncated` when the soft time cap
|
|
297
|
+
is exceeded.
|
|
298
|
+
|
|
202
299
|
### `get(id)` / `forget(id)`
|
|
203
300
|
|
|
204
301
|
Fetch one memory; soft-delete (cascades edges, rebalances the tree).
|
|
@@ -236,7 +333,7 @@ await dimple.index({
|
|
|
236
333
|
maxChunkBytes?: 8 * 1024,
|
|
237
334
|
extensions?: [".ts", ".tsx", ".js", ".jsx", …],
|
|
238
335
|
});
|
|
239
|
-
// → { files, indexed,
|
|
336
|
+
// → { files, indexed, updated, reconciled, skipped, commit, unsupported, failed }
|
|
240
337
|
```
|
|
241
338
|
|
|
242
339
|
- Tree-sitter parse (TypeScript/JavaScript/TSX/JSX) → the extractor
|
|
@@ -247,8 +344,27 @@ await dimple.index({
|
|
|
247
344
|
- AST-derived graph: relative imports resolve to indexed files and
|
|
248
345
|
class→method containment become **confirmed `memory_supports` edges**
|
|
249
346
|
(`source: "rule"`)
|
|
250
|
-
- **Idempotent**: keys are cwd-anchored + deterministic
|
|
251
|
-
|
|
347
|
+
- **Idempotent + upserting**: keys are cwd-anchored + deterministic
|
|
348
|
+
(chunk keys drop the line number, so edits ABOVE a definition update
|
|
349
|
+
it instead of duplicating it). Re-indexing skips unchanged units
|
|
350
|
+
(`skipped`), and changed units are **superseded + rewritten under the
|
|
351
|
+
same key** (`updated`) with a confirmed identity `memory_supersedes`
|
|
352
|
+
edge — stale copies never accumulate
|
|
353
|
+
- **Edges re-form after `forget`**: phase-2 resolves endpoints by logical
|
|
354
|
+
key against the store (not just the current run), so a re-created unit
|
|
355
|
+
re-connects to its pre-existing dependencies
|
|
356
|
+
- **Reconcile + rename detection**: units whose source vanished (deleted
|
|
357
|
+
file/definition) are demoted (`reconciled`, `source_removed` — gone
|
|
358
|
+
from search; domain-scoped to the walked roots, so `index lib` never
|
|
359
|
+
touches other files). A vanished unit whose embedding is a near-duplicate
|
|
360
|
+
of a new unit becomes a PENDING `memory_supersedes` (reason rename) —
|
|
361
|
+
`graph confirm` completes it. Code units are TTL-exempt
|
|
362
|
+
(`ttlSecs: null` — the index owns their lifecycle); `commit` stamps the
|
|
363
|
+
repo HEAD into every unit's metadata as `codeVersion`
|
|
364
|
+
- **`code_calls` edges**: every call site inside a definition resolves to
|
|
365
|
+
the callee's chunk (same file, then imported files) → confirmed edges —
|
|
366
|
+
`explain(id, { edgeKinds: ["code_calls"] })` gives one-hop impact
|
|
367
|
+
analysis
|
|
252
368
|
- Per-file failures are collected, never fatal
|
|
253
369
|
|
|
254
370
|
### `updateEmbedding(id, embedding, modelKey?)` / `embed(text)`
|
|
@@ -264,13 +380,19 @@ requires the `llm` config section) or the graph repair pass. Each call
|
|
|
264
380
|
gets a fresh run id (always executes). Failures are typed:
|
|
265
381
|
`Sdk.LLMNotConfigured`, `Sdk.JobFailed`.
|
|
266
382
|
|
|
267
|
-
### `explain({ kind: "memory" | "topic" | "concept", id })`
|
|
383
|
+
### `explain({ kind: "memory" | "topic" | "concept", id }, options?)`
|
|
268
384
|
|
|
269
385
|
Graph neighborhood traversal — `{ nodes: [{ kind, id, depth, edgeKind }] }`.
|
|
386
|
+
`options.edgeKinds` scopes the answer (memory-only navigation: pass the
|
|
387
|
+
`memory_*` kinds; `code_calls` shows the call graph).
|
|
270
388
|
|
|
271
389
|
### `health(modelKey)` / `graph.*`
|
|
272
390
|
|
|
273
391
|
- `health` — topic-tree invariant sweep (`warnCount` + violation strings)
|
|
392
|
+
- `graph.confirm(id)` / `graph.reject(id)` / `graph.expire(id)` — the
|
|
393
|
+
pending-edge lifecycle: promote to confirmed (a `memory_supersedes`
|
|
394
|
+
confirm demotes the old endpoint in the same transaction), reject, or
|
|
395
|
+
soft-delete. `Sdk.EdgeNotFound` for stale ids
|
|
274
396
|
- `graph.addEdge({ fromMemoryId, toMemoryId, edgeKind: "memory_supports" |
|
|
275
397
|
"memory_supersedes" | "memory_contradicts", source: "rule" | "llm" |
|
|
276
398
|
"agent", weight?, confidence?, confirmed? })` — explicit edges; typed
|
|
@@ -278,6 +400,42 @@ Graph neighborhood traversal — `{ nodes: [{ kind, id, depth, edgeKind }] }`.
|
|
|
278
400
|
- `graph.candidates()` — pending structural candidates + isolated count
|
|
279
401
|
- `graph.coverage(modelKey)` — coverage %, pending, concept count
|
|
280
402
|
|
|
403
|
+
### `reload(input?)` — apply a config change
|
|
404
|
+
|
|
405
|
+
Pauses and re-boots with the re-resolved config:
|
|
406
|
+
|
|
407
|
+
1. the config source is re-resolved **typed** — a `dimple.jsonc` path is
|
|
408
|
+
re-read from disk, an inline object re-validated (a bad config rejects
|
|
409
|
+
the reload with **zero impact** — nothing pauses);
|
|
410
|
+
2. an **identical** config resolves as `{ reloaded: false }` — no pause,
|
|
411
|
+
no restart (safe to call from a file watcher);
|
|
412
|
+
3. new calls wait, in-flight calls settle on the old stack;
|
|
413
|
+
4. a fresh stack boots (new worker fleet, embedders, logger — jobs are
|
|
414
|
+
durable, so an interrupted handler's lease expires and re-claims after
|
|
415
|
+
boot);
|
|
416
|
+
5. the swap: the old stack is disposed, the new one serves.
|
|
417
|
+
|
|
418
|
+
Resolves with `{ config, changed, reloaded }` — `changed` flags which
|
|
419
|
+
sections actually moved (`storeUrl` / `jobsUrl` / `rest`).
|
|
420
|
+
|
|
421
|
+
Works for `{ config: … }`-built dimples (reload with no argument re-reads
|
|
422
|
+
the original source). `{ core: … }`-built dimples have no config source
|
|
423
|
+
and reject with `Sdk.Reload`. Boot failure leaves the previous system
|
|
424
|
+
running (blue-green); a drain that cannot settle in-flight calls (30s)
|
|
425
|
+
rejects with `Sdk.Reload` and nothing is torn down.
|
|
426
|
+
|
|
427
|
+
**What happens to data when the database URL changes:** the old database
|
|
428
|
+
file is preserved on disk but no longer served — nothing is migrated or
|
|
429
|
+
merged, the new db is bootstrapped empty (a warning is logged for the
|
|
430
|
+
switch; roll back with `reload(oldConfig)`). Pending jobs in a switched
|
|
431
|
+
`jobs.dbUrl` stay durable in the old db, re-claimable by switching back
|
|
432
|
+
or draining it externally.
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
await dimple.reload(); // re-read dimple.jsonc
|
|
436
|
+
await dimple.reload({ storeUrl: "file:…", … }); // or a fresh config
|
|
437
|
+
```
|
|
438
|
+
|
|
281
439
|
### `dispose()`
|
|
282
440
|
|
|
283
441
|
Stops the worker, closes sinks + the store. Idempotent; post-dispose calls
|
|
@@ -326,6 +484,70 @@ query history, persisted asynchronously (never blocks the query).
|
|
|
326
484
|
| Grammar WASMs | `~/.cache/dimple/tree-sitter/` (downloaded once, offline after) |
|
|
327
485
|
| ONNX model | bun/npm cache (~90 MB, first use) |
|
|
328
486
|
|
|
487
|
+
## Changelog
|
|
488
|
+
|
|
489
|
+
Every release creates a GitHub Release with the per-version changes (the
|
|
490
|
+
tag-based publish workflow): repo → **Releases** page.
|
|
491
|
+
|
|
492
|
+
## Agent integration patterns
|
|
493
|
+
|
|
494
|
+
These are the shapes that work well when the consumer is another agent
|
|
495
|
+
(a harness) rather than a human with a REPL.
|
|
496
|
+
|
|
497
|
+
### Session-start injection
|
|
498
|
+
|
|
499
|
+
The guaranteed-usage path: a warmup query returns a compact memory block
|
|
500
|
+
the harness injects as context, and the pending-graph review list rides
|
|
501
|
+
along:
|
|
502
|
+
|
|
503
|
+
```ts
|
|
504
|
+
const [context, pending] = await Promise.all([
|
|
505
|
+
dimple.query("ongoing work and decisions", { limit: 8, budget: { maxChars: 4000 } }),
|
|
506
|
+
dimple.graph.candidates(),
|
|
507
|
+
]);
|
|
508
|
+
// context → inject the top-N content strings into the system prompt
|
|
509
|
+
// pending → offer each candidate as a confirm/reject decision
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
`graph.candidates()` pending `memory_supersedes` edges from the rename
|
|
513
|
+
scanner (rule 0.9x near-duplicates) are the highest-value batch to review.
|
|
514
|
+
|
|
515
|
+
### Supersedes recipe
|
|
516
|
+
|
|
517
|
+
`addEdge({ … edgeKind: "memory_supersedes", confirmed: true })` performs
|
|
518
|
+
the full state flip in one transaction (the old memory demotes
|
|
519
|
+
immediately) — it is safe to use directly. For human-gated flows the
|
|
520
|
+
pending → confirm pair keeps the old memory ranking until a reviewer
|
|
521
|
+
agrees:
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
const edge = await dimple.graph.addEdge({
|
|
525
|
+
fromMemoryId: oldId,
|
|
526
|
+
toMemoryId: newId,
|
|
527
|
+
edgeKind: "memory_supersedes",
|
|
528
|
+
source: "agent",
|
|
529
|
+
}); // pending — nothing changed yet
|
|
530
|
+
await dimple.graph.confirm(edge.id); // ← the flip happens here
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Namespaces as the code/message partition
|
|
534
|
+
|
|
535
|
+
Until a query-time content-type filter exists, namespaces give clean
|
|
536
|
+
isolation: `index({ paths, namespace: "code" })` and
|
|
537
|
+
`write({ …, namespace: "messages" })` partition the store; query with
|
|
538
|
+
`namespace: "code"` for code and `namespace: "messages"` for
|
|
539
|
+
conversations. (`query({ contentType })` now does the same within one
|
|
540
|
+
namespace — the two levers compose.)
|
|
541
|
+
|
|
542
|
+
### Remember
|
|
543
|
+
|
|
544
|
+
- `dimple.reload(newConfig)` swaps the whole stack blue-green (validate →
|
|
545
|
+
pause → boot → drain → swap); a failed boot leaves the old stack
|
|
546
|
+
serving.
|
|
547
|
+
- JSON output is compact by default: CLI `--json` omits embeddings (pass
|
|
548
|
+
`--embedding` to include them); the SDK always keeps them on
|
|
549
|
+
`memory.embedding`.
|
|
550
|
+
|
|
329
551
|
## Notes
|
|
330
552
|
|
|
331
553
|
- Determinism: same data + same config ⇒ same tree, same clusters, same
|
package/dist/dimple.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
2
|
import type { QueryProfile } from "./internal/retriever/index.js";
|
|
3
3
|
import type { CoreInterface } from "./internal/core/index.js";
|
|
4
4
|
import type { CodeExtractor } from "./internal/extractor/index.js";
|
|
5
5
|
import type { CodeParser } from "./internal/parser/index.js";
|
|
6
|
+
import type { DimpleConfig } from "./internal/config/index.js";
|
|
6
7
|
import type { AddEdgeInput, AugmentedMemory, ExplainResult, GraphCandidatesResult, GraphCoverage, MemoryUnit, TopicHealth, WriteMemoryInput } from "./internal/store/index.js";
|
|
8
|
+
import type { ConfigInput } from "./config.ts";
|
|
7
9
|
import { type DimpleError } from "./errors.ts";
|
|
8
10
|
/** Options for a `query` call — mirrors the hybrid retriever's options. */
|
|
9
11
|
export interface QueryOptions {
|
|
@@ -16,6 +18,19 @@ export interface QueryOptions {
|
|
|
16
18
|
readonly topics?: {
|
|
17
19
|
readonly topicKeys?: ReadonlyArray<string>;
|
|
18
20
|
};
|
|
21
|
+
/** Content-type filter: one kind or a list (e.g. `"text/x-typescript"`
|
|
22
|
+
* or `["text/plain", "text/markdown"]`) — the code-vs-message
|
|
23
|
+
* separation lever at query time. */
|
|
24
|
+
readonly contentType?: string | readonly string[];
|
|
25
|
+
/** Retrieval context budget (LLM-facing guardrail): `maxUnits` caps the
|
|
26
|
+
* row count, `maxChars` truncates result content to fit the total char
|
|
27
|
+
* budget, `maxMs` marks the result truncated when the query exceeds it.
|
|
28
|
+
* Truncation is surfaced as `results.truncated` on the returned array. */
|
|
29
|
+
readonly budget?: {
|
|
30
|
+
readonly maxUnits?: number;
|
|
31
|
+
readonly maxChars?: number;
|
|
32
|
+
readonly maxMs?: number;
|
|
33
|
+
};
|
|
19
34
|
}
|
|
20
35
|
/** Options for a `write` call. */
|
|
21
36
|
export interface WriteOptions {
|
|
@@ -49,22 +64,58 @@ export interface IndexResult {
|
|
|
49
64
|
readonly files: number;
|
|
50
65
|
/** Memory units written (file unit + extracted chunks). */
|
|
51
66
|
readonly indexed: number;
|
|
52
|
-
/** Units
|
|
67
|
+
/** Units whose active copy changed content — the old unit was
|
|
68
|
+
* superseded (confirmed `memory_supersedes` edge) and the new unit
|
|
69
|
+
* written under the same key. */
|
|
70
|
+
readonly updated: number;
|
|
71
|
+
/** Units whose source vanished from disk (deleted/moved file or
|
|
72
|
+
* definition) — demoted to superseded (`source_removed`); they no
|
|
73
|
+
* longer match queries. */
|
|
74
|
+
readonly reconciled: number;
|
|
75
|
+
/** The repo HEAD at index time (null outside a git tree) — stamped into
|
|
76
|
+
* every file/chunk unit's metadata as `codeVersion`. */
|
|
77
|
+
/** Units skipped as already-indexed (unchanged content under the same
|
|
78
|
+
* logical key). */
|
|
53
79
|
readonly skipped: number;
|
|
54
80
|
/** Files skipped because the extension is not indexable. */
|
|
55
81
|
readonly unsupported: number;
|
|
82
|
+
readonly commit: string | null;
|
|
56
83
|
/** Files that could not be walked/read/parsed/extracted/written. */
|
|
57
84
|
readonly failed: readonly {
|
|
58
85
|
readonly filePath: string;
|
|
59
86
|
readonly message: string;
|
|
60
87
|
}[];
|
|
61
88
|
}
|
|
89
|
+
/** What a `reload` actually changed — so callers can react (alert, log,
|
|
90
|
+
* refuse) instead of guessing. */
|
|
91
|
+
export interface ReloadChanges {
|
|
92
|
+
/** The store database URL changed — the new db was bootstrapped (empty
|
|
93
|
+
* schema); the old db file is preserved on disk but no longer served.
|
|
94
|
+
* Nothing is migrated or merged. */
|
|
95
|
+
readonly storeUrl: boolean;
|
|
96
|
+
/** The jobs database URL changed — pending jobs stay durable in the
|
|
97
|
+
* old db (re-claimable by switching back or draining it externally). */
|
|
98
|
+
readonly jobsUrl: boolean;
|
|
99
|
+
/** Any other section changed (models, topics, graph, logging, …). */
|
|
100
|
+
readonly rest: boolean;
|
|
101
|
+
}
|
|
102
|
+
/** Result of a `reload`. */
|
|
103
|
+
export interface ReloadResult {
|
|
104
|
+
readonly config: DimpleConfig;
|
|
105
|
+
readonly changed: ReloadChanges;
|
|
106
|
+
/** `false` when the resolved config was identical to the live one —
|
|
107
|
+
* nothing was paused, booted, or restarted (safe for watchers). */
|
|
108
|
+
readonly reloaded: boolean;
|
|
109
|
+
}
|
|
62
110
|
/**
|
|
63
111
|
* The promise-based SDK facade — the ONLY surface app code touches.
|
|
64
112
|
* Every method runs the underlying Effect at the boundary, remaps errors
|
|
65
113
|
* onto the closed `DimpleError` hierarchy, and never leaks Effect types.
|
|
66
114
|
*/
|
|
67
115
|
export interface Dimple {
|
|
116
|
+
/** The config-default embedding model's key — what `dream`/`repair`/
|
|
117
|
+
* `stats` use when no model is passed. */
|
|
118
|
+
readonly defaultModelId: string;
|
|
68
119
|
readonly write: (input: WriteMemoryInput, options?: WriteOptions) => Promise<MemoryUnit>;
|
|
69
120
|
readonly get: (id: string) => Promise<MemoryUnit>;
|
|
70
121
|
readonly query: (text: string, options?: QueryOptions) => Promise<AugmentedMemory[]>;
|
|
@@ -83,19 +134,66 @@ export interface Dimple {
|
|
|
83
134
|
readonly embed: (text: string) => Promise<number[]>;
|
|
84
135
|
readonly dream: (modelKey: string) => Promise<void>;
|
|
85
136
|
readonly repair: (modelKey: string) => Promise<void>;
|
|
137
|
+
/** The store's tables + columns (snake_case DB names — the SDK exposes
|
|
138
|
+
* camelCase domain names; `dimple db-schema` prints this map). */
|
|
139
|
+
readonly dbSchema: () => Promise<ReadonlyArray<{
|
|
140
|
+
readonly table: string;
|
|
141
|
+
readonly columns: ReadonlyArray<{
|
|
142
|
+
readonly name: string;
|
|
143
|
+
readonly type: string;
|
|
144
|
+
}>;
|
|
145
|
+
}>>;
|
|
146
|
+
/** Options for `explain` — pass `edgeKinds` for a scoped answer (the
|
|
147
|
+
* common ask is memory-only navigation: the three `memory_*` kinds keep
|
|
148
|
+
* the topic/concept legs out). */
|
|
86
149
|
readonly explain: (input: {
|
|
87
150
|
readonly kind: "memory" | "topic" | "concept";
|
|
88
151
|
readonly id: string;
|
|
152
|
+
}, options?: {
|
|
153
|
+
readonly edgeKinds?: readonly string[];
|
|
154
|
+
readonly maxDepth?: number;
|
|
155
|
+
readonly maxNeighbors?: number;
|
|
156
|
+
readonly maxTotal?: number;
|
|
157
|
+
readonly includePending?: boolean;
|
|
89
158
|
}) => Promise<ExplainResult>;
|
|
90
159
|
readonly health: (modelKey: string) => Promise<TopicHealth>;
|
|
91
160
|
readonly graph: {
|
|
92
161
|
readonly addEdge: (input: AddEdgeInput) => Promise<unknown>;
|
|
93
162
|
readonly candidates: () => Promise<GraphCandidatesResult>;
|
|
94
163
|
readonly coverage: (modelKey: string) => Promise<GraphCoverage>;
|
|
164
|
+
/** Promote a pending edge to confirmed (for `memory_supersedes` the
|
|
165
|
+
* old endpoint flips to superseded in the same transaction). */
|
|
166
|
+
readonly confirm: (id: string) => Promise<void>;
|
|
167
|
+
/** Reject a pending edge (removed from the pending review set). */
|
|
168
|
+
readonly reject: (id: string) => Promise<void>;
|
|
169
|
+
/** Soft-delete an edge (`expired_at` set; excluded from traversal). */
|
|
170
|
+
readonly expire: (id: string) => Promise<void>;
|
|
95
171
|
};
|
|
172
|
+
/**
|
|
173
|
+
* Apply a config change: pause (new calls wait, in-flight calls settle),
|
|
174
|
+
* boot a fresh stack with the re-resolved config, and swap it in.
|
|
175
|
+
*
|
|
176
|
+
* With no argument the ORIGINAL config source is re-resolved — a
|
|
177
|
+
* `dimple.jsonc` FILE PATH is re-read from disk, an inline object is
|
|
178
|
+
* re-validated. A config that fails validation or boot rejects the
|
|
179
|
+
* reload and leaves the current system untouched. An identical config
|
|
180
|
+
* resolves as `{ reloaded: false }` — no pause, no restart.
|
|
181
|
+
*/
|
|
182
|
+
readonly reload: (input?: ConfigInput) => Promise<ReloadResult>;
|
|
96
183
|
/** Stops the worker, closes the sinks and the store — idempotent. */
|
|
97
184
|
readonly dispose: () => Promise<void>;
|
|
98
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* The live handles the facade reads at call time. `makeDimple` swaps the
|
|
188
|
+
* stack under this state on `reload`, so methods always hit the stack that
|
|
189
|
+
* is current when they run.
|
|
190
|
+
*/
|
|
191
|
+
export interface FacadeState {
|
|
192
|
+
readonly run: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>;
|
|
193
|
+
readonly core: () => CoreInterface;
|
|
194
|
+
readonly parser: () => CodeParser["Service"];
|
|
195
|
+
readonly extractor: () => CodeExtractor["Service"];
|
|
196
|
+
}
|
|
99
197
|
/** Build the promise facade over the booted core + runtime. */
|
|
100
|
-
export declare const makeFacade:
|
|
198
|
+
export declare const makeFacade: (state: FacadeState, reload: (input?: ConfigInput) => Promise<ReloadResult>, dispose: () => Promise<void>) => Dimple;
|
|
101
199
|
export type { DimpleError };
|
package/dist/errors.d.ts
CHANGED
|
@@ -45,19 +45,41 @@ declare const DuplicateEdgeError_base: Schema.Class<DuplicateEdgeError, Schema.T
|
|
|
45
45
|
/** A duplicate memory↔memory edge (same from/to/kind already exists). */
|
|
46
46
|
export declare class DuplicateEdgeError extends DuplicateEdgeError_base {
|
|
47
47
|
}
|
|
48
|
+
declare const EdgeNotFoundError_base: Schema.Class<EdgeNotFoundError, Schema.TaggedStruct<"Sdk.EdgeNotFound", {
|
|
49
|
+
readonly id: Schema.optional<Schema.String>;
|
|
50
|
+
readonly message: Schema.String;
|
|
51
|
+
}>, import("effect/Cause").YieldableError>;
|
|
52
|
+
/** The edge does not exist (confirm/reject/expire on a stale id). */
|
|
53
|
+
export declare class EdgeNotFoundError extends EdgeNotFoundError_base {
|
|
54
|
+
}
|
|
48
55
|
declare const StoreFailure_base: Schema.Class<StoreFailure, Schema.TaggedStruct<"Sdk.StoreFailure", {
|
|
49
56
|
readonly message: Schema.String;
|
|
50
57
|
}>, import("effect/Cause").YieldableError>;
|
|
51
58
|
/** Anything else — the typed fallback that keeps the union closed. */
|
|
52
59
|
export declare class StoreFailure extends StoreFailure_base {
|
|
53
60
|
}
|
|
61
|
+
declare const ReloadError_base: Schema.Class<ReloadError, Schema.TaggedStruct<"Sdk.Reload", {
|
|
62
|
+
readonly message: Schema.String;
|
|
63
|
+
}>, import("effect/Cause").YieldableError>;
|
|
64
|
+
/** `reload` was unavailable (no config source, disposed) or failed to
|
|
65
|
+
* drain in-flight calls before the swap. */
|
|
66
|
+
export declare class ReloadError extends ReloadError_base {
|
|
67
|
+
}
|
|
68
|
+
declare const EmbeddingFailure_base: Schema.Class<EmbeddingFailure, Schema.TaggedStruct<"Sdk.Embedding", {
|
|
69
|
+
readonly message: Schema.String;
|
|
70
|
+
}>, import("effect/Cause").YieldableError>;
|
|
71
|
+
/** Embedding pipeline failure — the `@huggingface/transformers` package is
|
|
72
|
+
* missing (local embeddings), the model failed to load, or the output
|
|
73
|
+
* dimensions mismatch the config. */
|
|
74
|
+
export declare class EmbeddingFailure extends EmbeddingFailure_base {
|
|
75
|
+
}
|
|
54
76
|
declare const InputError_base: Schema.Class<InputError, Schema.TaggedStruct<"Sdk.InputError", {
|
|
55
77
|
readonly message: Schema.String;
|
|
56
78
|
}>, import("effect/Cause").YieldableError>;
|
|
57
79
|
/** Invalid SDK call input (bad `limit`, `k`, profile shape, …). */
|
|
58
80
|
export declare class InputError extends InputError_base {
|
|
59
81
|
}
|
|
60
|
-
export type DimpleError = ConfigError | NotFoundError | DuplicateKeyError | DuplicateEdgeError | LLMNotConfiguredError | JobFailedError | StoreFailure | InputError;
|
|
82
|
+
export type DimpleError = ConfigError | NotFoundError | DuplicateKeyError | DuplicateEdgeError | EdgeNotFoundError | LLMNotConfiguredError | EmbeddingFailure | JobFailedError | StoreFailure | ReloadError | InputError;
|
|
61
83
|
/** Map an underlying failure onto the closed SDK hierarchy. */
|
|
62
84
|
export declare const remapError: (error: unknown) => DimpleError;
|
|
63
85
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { makeDimple, type MakeDimpleOptions } from "./make.ts";
|
|
2
2
|
export { type ConfigInput } from "./config.ts";
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
3
|
+
export type { DimpleConfig } from "./internal/config/index.js";
|
|
4
|
+
export { type Dimple, type QueryOptions, type IndexOptions, type IndexResult, type ReloadChanges, type ReloadResult, } from "./dimple.ts";
|
|
5
|
+
export { ConfigError, NotFoundError, DuplicateKeyError, DuplicateEdgeError, EdgeNotFoundError, LLMNotConfiguredError, JobFailedError, StoreFailure, ReloadError, EmbeddingFailure, InputError, remapError, type DimpleError, } from "./errors.ts";
|