@ponraaj/dimple-sdk 0.0.0-alpha.7 → 0.0.0-alpha.8
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 +14 -13
- package/dist/dimple.d.ts +6 -0
- package/dist/index.js +187 -45
- package/dist/internal/embedder/auto-install.d.ts +8 -33
- package/dist/internal/installer/index.d.ts +26 -0
- package/dist/internal/store/service.d.ts +8 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,18 +54,19 @@ promise with a closed `_tag` (`"Sdk.NotFound"`, `"Sdk.InputError"`, …);
|
|
|
54
54
|
|
|
55
55
|
## API surface
|
|
56
56
|
|
|
57
|
-
| Method | What it does
|
|
58
|
-
| --------------------------------------------------------- |
|
|
59
|
-
| `write(input, { embed })` | Store a memory (idempotent per `logicalKey`)
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
57
|
+
| Method | What it does |
|
|
58
|
+
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
59
|
+
| `write(input, { embed })` | Store a memory (idempotent per `logicalKey`) |
|
|
60
|
+
| `writeMany(units, { embed })` | Bulk ingest: batch insert + batch embed + routing in one pass (skips the per-write candidate scan — quadratic under bulk; run the dream job afterwards for graph edges) |
|
|
61
|
+
| `get(id)` / `forget(id)` | Fetch / remove by id or logical key |
|
|
62
|
+
| `query(text, options)` | Hybrid search — profiles, budgets, content-type filter |
|
|
63
|
+
| `updateEmbedding(id, vec, modelKey?)` | Attach an external embedding |
|
|
64
|
+
| `index({ paths })` | Codebase ingestion (idempotent re-index) |
|
|
65
|
+
| `graph.addEdge/confirm/reject/expire/candidates/coverage` | Memory relationships: supports / supersedes / contradicts |
|
|
66
|
+
| `explain({ kind, id }, options)` | Bounded graph neighborhood |
|
|
67
|
+
| `dream(modelKey?)` / `repair(modelKey?)` | Consolidation + maintenance jobs |
|
|
68
|
+
| `health(modelKey?)` | Topic-tree invariant health |
|
|
69
|
+
| `reload(input?)` | Blue-green config reload |
|
|
69
70
|
|
|
70
71
|
Errors are a closed hierarchy (`InputError`, `NotFound`, `DuplicateKey`,
|
|
71
72
|
`JobFailed`, `LLMNotConfigured`, `StoreFailure`) — see the docs site.
|
|
@@ -93,5 +94,5 @@ and in `docs/MCP.md` in the repository.
|
|
|
93
94
|
|
|
94
95
|
## Changelog
|
|
95
96
|
|
|
96
|
-
[Release history](https://dimple.ponraaj.me/changelog/sdk) · repository
|
|
97
|
+
[Release history](https://dimple.ponraaj.me/changelog/sdk/) · repository
|
|
97
98
|
[`CHANGELOG.md`](./CHANGELOG.md)
|
package/dist/dimple.d.ts
CHANGED
|
@@ -117,6 +117,12 @@ export interface Dimple {
|
|
|
117
117
|
* `stats` use when no model is passed. */
|
|
118
118
|
readonly defaultModelId: string;
|
|
119
119
|
readonly write: (input: WriteMemoryInput, options?: WriteOptions) => Promise<MemoryUnit>;
|
|
120
|
+
/** Bulk write: batch insert + batch embed + topic routing in ONE pass.
|
|
121
|
+
* Skips the per-write graph candidate scan (O(leaf size) per write —
|
|
122
|
+
* quadratic under bulk ingest); callers that need near-duplicate/
|
|
123
|
+
* conflict edges on bulk content should run the dream job afterwards.
|
|
124
|
+
* All-or-nothing: a duplicate logical key rejects the whole batch. */
|
|
125
|
+
readonly writeMany: (units: readonly WriteMemoryInput[], options?: WriteOptions) => Promise<readonly MemoryUnit[]>;
|
|
120
126
|
readonly get: (id: string) => Promise<MemoryUnit>;
|
|
121
127
|
readonly query: (text: string, options?: QueryOptions) => Promise<AugmentedMemory[]>;
|
|
122
128
|
readonly forget: (id: string) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -766,6 +766,7 @@ import { Effect as Effect7, Schema as Schema7 } from "effect";
|
|
|
766
766
|
import { parse, parseTree, printParseErrorCode } from "jsonc-parser";
|
|
767
767
|
var CONFIG_FILE_NAMES = ["dimple.jsonc", "dimple.json"];
|
|
768
768
|
var GLOBAL_DIR = ".config/dimple";
|
|
769
|
+
var resolveCwd = () => process.env["DIMPLE_CWD"] ?? process.cwd();
|
|
769
770
|
var defaults = {
|
|
770
771
|
storeUrl: "file:./.dimple/dimple.db",
|
|
771
772
|
defaultK: 60,
|
|
@@ -871,7 +872,7 @@ var loadFs = () => Effect7.tryPromise(() => import("node:fs"));
|
|
|
871
872
|
var findProjectConfig = () => Effect7.gen(function* () {
|
|
872
873
|
const fs = yield* loadFs();
|
|
873
874
|
const path = yield* Effect7.tryPromise(() => import("node:path"));
|
|
874
|
-
let dir =
|
|
875
|
+
let dir = resolveCwd();
|
|
875
876
|
for (let i = 0;i < 20; i++) {
|
|
876
877
|
for (const name of CONFIG_FILE_NAMES) {
|
|
877
878
|
const p = path.join(dir, name);
|
|
@@ -940,6 +941,23 @@ var resolveEnvVars = (value) => {
|
|
|
940
941
|
}
|
|
941
942
|
return value;
|
|
942
943
|
};
|
|
944
|
+
var resolveCwdUrls = (value) => {
|
|
945
|
+
const cwd = process.env["DIMPLE_CWD"];
|
|
946
|
+
if (cwd === undefined)
|
|
947
|
+
return value;
|
|
948
|
+
if (typeof value === "string" && value.startsWith("file:./")) {
|
|
949
|
+
return `file:${cwd}${cwd.endsWith("/") ? "" : "/"}${value.slice("file:./".length)}`;
|
|
950
|
+
}
|
|
951
|
+
if (Array.isArray(value))
|
|
952
|
+
return value.map(resolveCwdUrls);
|
|
953
|
+
if (value !== null && typeof value === "object") {
|
|
954
|
+
const result = {};
|
|
955
|
+
for (const [k, v] of Object.entries(value))
|
|
956
|
+
result[k] = resolveCwdUrls(v);
|
|
957
|
+
return result;
|
|
958
|
+
}
|
|
959
|
+
return value;
|
|
960
|
+
};
|
|
943
961
|
var deepMerge = (base, over) => {
|
|
944
962
|
const out = { ...base };
|
|
945
963
|
for (const [key, value] of Object.entries(over)) {
|
|
@@ -957,7 +975,7 @@ var loadConfigFromFile = (path) => Effect7.gen(function* () {
|
|
|
957
975
|
const parsed = yield* parseJsoncEffect(content, (cause) => new Error(`config parse ${path}: ${cause.message}`));
|
|
958
976
|
const merged = deepMerge(defaults, parsed);
|
|
959
977
|
const resolved = resolveEnvVars(merged);
|
|
960
|
-
return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolved).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
|
|
978
|
+
return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolveCwdUrls(resolved)).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
|
|
961
979
|
});
|
|
962
980
|
var loadConfig = (overrides) => Effect7.gen(function* () {
|
|
963
981
|
const fileParts = yield* loadFromFiles();
|
|
@@ -970,7 +988,7 @@ var loadConfig = (overrides) => Effect7.gen(function* () {
|
|
|
970
988
|
if (overrides)
|
|
971
989
|
merged = deepMerge(merged, overrides);
|
|
972
990
|
const resolved = resolveEnvVars(merged);
|
|
973
|
-
return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolved).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
|
|
991
|
+
return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolveCwdUrls(resolved)).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
|
|
974
992
|
});
|
|
975
993
|
|
|
976
994
|
// ../config/src/service.ts
|
|
@@ -1236,7 +1254,7 @@ var makeBackend = (url, client, db, migrationFolder) => ({
|
|
|
1236
1254
|
ORDER BY f.rank
|
|
1237
1255
|
LIMIT ?`,
|
|
1238
1256
|
args: [
|
|
1239
|
-
`"${
|
|
1257
|
+
query.split(/\s+/).filter((token) => token.length > 0).map((token) => `"${token.replaceAll('"', '""')}"`).join(" "),
|
|
1240
1258
|
namespace,
|
|
1241
1259
|
...topicKeys && topicKeys.length > 0 ? [...topicKeys, modelKey ?? ""] : [],
|
|
1242
1260
|
...typeof contentType === "string" ? [contentType] : contentType ?? [],
|
|
@@ -4610,6 +4628,58 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
|
|
|
4610
4628
|
embeddingModel: undefined
|
|
4611
4629
|
};
|
|
4612
4630
|
});
|
|
4631
|
+
const writeMany = Effect26.fn("Store.writeMany")(function* (inputs) {
|
|
4632
|
+
if (inputs.length === 0)
|
|
4633
|
+
return [];
|
|
4634
|
+
for (const input of inputs) {
|
|
4635
|
+
if (input.content.includes("\x00")) {
|
|
4636
|
+
return yield* new StoreError({
|
|
4637
|
+
message: "Store.writeMany: content contains a NUL byte (libSQL would silently truncate it)"
|
|
4638
|
+
});
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
const now = Date.now();
|
|
4642
|
+
const rows = inputs.map((input) => {
|
|
4643
|
+
const metadataBlob = input.typedMetadata !== undefined ? mergeMetadataBlob(input.metadata, input.typedMetadata) : input.metadata ?? null;
|
|
4644
|
+
return {
|
|
4645
|
+
id: crypto.randomUUID(),
|
|
4646
|
+
logicalKey: input.logicalKey,
|
|
4647
|
+
content: input.content,
|
|
4648
|
+
contentType: input.contentType ?? "text/plain",
|
|
4649
|
+
createdAt: now,
|
|
4650
|
+
ttlSecs: input.ttlSecs === null ? null : input.ttlSecs ?? defaultTtlSecs,
|
|
4651
|
+
namespace: input.namespace ?? "default",
|
|
4652
|
+
metadata: metadataBlob
|
|
4653
|
+
};
|
|
4654
|
+
});
|
|
4655
|
+
yield* Effect26.tryPromise({
|
|
4656
|
+
try: () => db.transaction(async (tx) => {
|
|
4657
|
+
await tx.insert(memories2).values(rows);
|
|
4658
|
+
await tx.insert(memoryEvents2).values(rows.map((row) => ({
|
|
4659
|
+
memoryId: row.id,
|
|
4660
|
+
eventType: "created",
|
|
4661
|
+
createdAt: now
|
|
4662
|
+
})));
|
|
4663
|
+
}),
|
|
4664
|
+
catch: (cause) => backend.isUniqueViolation(cause) ? new DuplicateActiveKey({
|
|
4665
|
+
logicalKey: inputs[0].logicalKey,
|
|
4666
|
+
namespace: inputs[0].namespace ?? "default"
|
|
4667
|
+
}) : new StoreError({ message: `Store.writeMany: ${cause}` })
|
|
4668
|
+
});
|
|
4669
|
+
return rows.map((row) => ({
|
|
4670
|
+
id: row.id,
|
|
4671
|
+
logicalKey: row.logicalKey,
|
|
4672
|
+
content: row.content,
|
|
4673
|
+
contentType: row.contentType,
|
|
4674
|
+
state: "active",
|
|
4675
|
+
createdAt: row.createdAt,
|
|
4676
|
+
ttlSecs: row.ttlSecs,
|
|
4677
|
+
namespace: row.namespace,
|
|
4678
|
+
metadata: row.metadata ?? undefined,
|
|
4679
|
+
embedding: undefined,
|
|
4680
|
+
embeddingModel: undefined
|
|
4681
|
+
}));
|
|
4682
|
+
});
|
|
4613
4683
|
const get = Effect26.fn("Store.get")(function* (id) {
|
|
4614
4684
|
const rows = yield* Effect26.tryPromise({
|
|
4615
4685
|
try: () => db.select(memoryFields).from(memories2).where(eq8(memories2.id, id)).limit(1),
|
|
@@ -4972,7 +5042,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
|
|
|
4972
5042
|
}
|
|
4973
5043
|
return yield* topicHealth(modelKey, topicsConfig).pipe(Effect26.provideService(Service2, backend));
|
|
4974
5044
|
});
|
|
4975
|
-
const updateEmbedding = Effect26.fn("Store.updateEmbedding")(function* (id, embedding, modelName) {
|
|
5045
|
+
const updateEmbedding = Effect26.fn("Store.updateEmbedding")(function* (id, embedding, modelName, options) {
|
|
4976
5046
|
const now = Date.now();
|
|
4977
5047
|
const affected = yield* Effect26.tryPromise({
|
|
4978
5048
|
try: () => db.transaction(async (tx) => {
|
|
@@ -5028,7 +5098,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
|
|
|
5028
5098
|
yield* backend.insertVector(id, embedding, modelConfig);
|
|
5029
5099
|
if (topicsEnabled) {
|
|
5030
5100
|
const routed = yield* routeInsert(id, embedding, modelConfig, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.updateEmbedding topic route: ${cause}` })));
|
|
5031
|
-
|
|
5101
|
+
if (options?.scanCandidates !== false) {
|
|
5102
|
+
yield* scanLeafCandidates(id, embedding, routed.topicKey, modelConfig);
|
|
5103
|
+
}
|
|
5032
5104
|
if (routed.overflow) {
|
|
5033
5105
|
const enqueueSplit = topicsOptions?.enqueueSplit;
|
|
5034
5106
|
if (enqueueSplit) {
|
|
@@ -5588,6 +5660,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
|
|
|
5588
5660
|
});
|
|
5589
5661
|
return {
|
|
5590
5662
|
write,
|
|
5663
|
+
writeMany,
|
|
5591
5664
|
writeSuperseding,
|
|
5592
5665
|
reconcile,
|
|
5593
5666
|
reconcileRenameCandidates,
|
|
@@ -6553,10 +6626,16 @@ import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
|
6553
6626
|
import { join as join2 } from "node:path";
|
|
6554
6627
|
import { pathToFileURL } from "node:url";
|
|
6555
6628
|
|
|
6556
|
-
// ../
|
|
6557
|
-
import { execFile } from "node:child_process";
|
|
6629
|
+
// ../installer/src/index.ts
|
|
6558
6630
|
import { existsSync } from "node:fs";
|
|
6559
6631
|
import { join } from "node:path";
|
|
6632
|
+
|
|
6633
|
+
class InstallerError extends Error {
|
|
6634
|
+
constructor(message) {
|
|
6635
|
+
super(message);
|
|
6636
|
+
this.name = "InstallerError";
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6560
6639
|
var detectPackageManager = (cwd) => {
|
|
6561
6640
|
let dir = cwd;
|
|
6562
6641
|
while (true) {
|
|
@@ -6571,38 +6650,74 @@ var detectPackageManager = (cwd) => {
|
|
|
6571
6650
|
return { pm: "npm", root: dir };
|
|
6572
6651
|
const parent = join(dir, "..");
|
|
6573
6652
|
if (parent === dir)
|
|
6574
|
-
return { pm: "
|
|
6653
|
+
return { pm: "npm", root: cwd };
|
|
6575
6654
|
dir = parent;
|
|
6576
6655
|
}
|
|
6577
6656
|
};
|
|
6578
|
-
var
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
"--no-audit",
|
|
6589
|
-
"--no-fund"
|
|
6590
|
-
];
|
|
6591
|
-
case "pnpm":
|
|
6592
|
-
return ["pnpm", "add", "@huggingface/transformers", "--ignore-scripts"];
|
|
6593
|
-
case "yarn":
|
|
6594
|
-
return ["yarn", "add", "@huggingface/transformers", "--ignore-scripts"];
|
|
6595
|
-
}
|
|
6657
|
+
var resolveExecutable = (pm, platform = process.platform) => platform === "win32" && pm !== "bun" ? `${pm}.cmd` : pm;
|
|
6658
|
+
var installArgs = (pm, packages, root) => {
|
|
6659
|
+
const argv = [pm === "npm" ? "install" : "add", ...packages];
|
|
6660
|
+
if (pm !== "bun")
|
|
6661
|
+
argv.push("--ignore-scripts");
|
|
6662
|
+
if (pm === "npm")
|
|
6663
|
+
argv.push("--no-audit", "--no-fund");
|
|
6664
|
+
if (pm === "pnpm" && existsSync(join(root, "pnpm-workspace.yaml")))
|
|
6665
|
+
argv.push("-w");
|
|
6666
|
+
return argv;
|
|
6596
6667
|
};
|
|
6597
|
-
var
|
|
6668
|
+
var installOnce = async (target, packages, opts) => {
|
|
6669
|
+
const { spawn } = await import("node:child_process");
|
|
6670
|
+
return new Promise((resolve, reject) => {
|
|
6671
|
+
const timeoutMs = opts.timeoutMs ?? 300000;
|
|
6672
|
+
const cmd = resolveExecutable(target.pm, opts.platform);
|
|
6673
|
+
const args = installArgs(target.pm, packages, target.root);
|
|
6674
|
+
const manual = `${cmd} ${args.join(" ")}`;
|
|
6675
|
+
const child = spawn(cmd, args, { cwd: target.root, stdio: "ignore" });
|
|
6676
|
+
let settled = false;
|
|
6677
|
+
const timer = setTimeout(() => {
|
|
6678
|
+
child.kill("SIGKILL");
|
|
6679
|
+
if (settled)
|
|
6680
|
+
return;
|
|
6681
|
+
settled = true;
|
|
6682
|
+
reject(new InstallerError(`auto-install timed out after ${timeoutMs} ms — the child was killed; run it manually: ${manual}`));
|
|
6683
|
+
}, timeoutMs);
|
|
6684
|
+
child.on("error", (cause) => {
|
|
6685
|
+
clearTimeout(timer);
|
|
6686
|
+
if (settled)
|
|
6687
|
+
return;
|
|
6688
|
+
settled = true;
|
|
6689
|
+
reject(new InstallerError(`auto-install failed to start (${manual}): ${cause.message} — run it manually`));
|
|
6690
|
+
});
|
|
6691
|
+
child.on("exit", (code, signal) => {
|
|
6692
|
+
clearTimeout(timer);
|
|
6693
|
+
if (settled)
|
|
6694
|
+
return;
|
|
6695
|
+
settled = true;
|
|
6696
|
+
if (code === 0) {
|
|
6697
|
+
resolve();
|
|
6698
|
+
return;
|
|
6699
|
+
}
|
|
6700
|
+
reject(new InstallerError(`auto-install failed (${manual} exited ${signal !== null ? `with ${signal}` : `code ${code ?? "?"}`}) — run it manually`));
|
|
6701
|
+
});
|
|
6702
|
+
});
|
|
6703
|
+
};
|
|
6704
|
+
var inflight = new Map;
|
|
6705
|
+
var installOptionalPeers = (packages, opts) => {
|
|
6706
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
6598
6707
|
const target = detectPackageManager(cwd);
|
|
6599
|
-
const
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6708
|
+
const key = `${target.root}\x00${packages.join(",")}`;
|
|
6709
|
+
const existing = inflight.get(key);
|
|
6710
|
+
if (existing !== undefined)
|
|
6711
|
+
return existing;
|
|
6712
|
+
const run = installOnce(target, packages, opts ?? {});
|
|
6713
|
+
inflight.set(key, run);
|
|
6714
|
+
run.then(() => inflight.delete(key), () => inflight.delete(key));
|
|
6715
|
+
return run;
|
|
6716
|
+
};
|
|
6717
|
+
// ../embedder/src/auto-install.ts
|
|
6718
|
+
var installTransformers = (cwd) => installOptionalPeers(["@huggingface/transformers"], { cwd, timeoutMs: 300000 }).catch((cause) => {
|
|
6719
|
+
throw new EmbeddingError({
|
|
6720
|
+
message: cause instanceof InstallerError ? `${cause.message} — or set \`autoInstall: false\` and configure a hosted embedding provider` : `auto-install of @huggingface/transformers failed: ${String(cause)} — run it manually ` + "or set `autoInstall: false` and configure a hosted embedding provider"
|
|
6606
6721
|
});
|
|
6607
6722
|
});
|
|
6608
6723
|
|
|
@@ -6667,9 +6782,12 @@ var make5 = (options) => {
|
|
|
6667
6782
|
}
|
|
6668
6783
|
return Effect32.gen(function* () {
|
|
6669
6784
|
yield* Effect32.logInfo("auto-installing @huggingface/transformers (first local-embed use — scripts skipped)").pipe(Effect32.annotateLogs({ scope: "@dimple/embedder", event: "embedder.auto-install" }));
|
|
6670
|
-
yield* Effect32.tryPromise(() => installTransformers(process.cwd())).pipe(Effect32.
|
|
6671
|
-
|
|
6672
|
-
|
|
6785
|
+
yield* Effect32.tryPromise(() => installTransformers(process.cwd())).pipe(Effect32.catch((cause) => {
|
|
6786
|
+
const inner = cause.cause;
|
|
6787
|
+
return Effect32.fail(inner instanceof EmbeddingError ? inner : new EmbeddingError({
|
|
6788
|
+
message: `auto-install failed: ${inner instanceof Error ? inner.message : String(cause)}`
|
|
6789
|
+
}));
|
|
6790
|
+
}));
|
|
6673
6791
|
return yield* Effect32.tryPromise(importFromDisk).pipe(Effect32.mapError(() => new EmbeddingError({
|
|
6674
6792
|
message: "@huggingface/transformers still unavailable after auto-install — run `bun add " + "@huggingface/transformers` manually (or set `autoInstall: false` and configure a hosted embedding provider)"
|
|
6675
6793
|
})));
|
|
@@ -8417,8 +8535,8 @@ var walkFiles = (paths2, extensions) => {
|
|
|
8417
8535
|
const files = [];
|
|
8418
8536
|
const errors3 = [];
|
|
8419
8537
|
let unsupported = 0;
|
|
8420
|
-
const resolved = paths2.map((p) => resolve(p));
|
|
8421
|
-
const root = commonRoot([...resolved, process.cwd()]);
|
|
8538
|
+
const resolved = paths2.map((p) => resolve(process.env["DIMPLE_CWD"] ?? process.cwd(), p));
|
|
8539
|
+
const root = commonRoot([...resolved, process.env["DIMPLE_CWD"] ?? process.cwd()]);
|
|
8422
8540
|
const visit = (path2) => {
|
|
8423
8541
|
let stat;
|
|
8424
8542
|
try {
|
|
@@ -8811,6 +8929,30 @@ var makeFacade = (state, reload, dispose) => {
|
|
|
8811
8929
|
return yield* invalid;
|
|
8812
8930
|
return yield* writeUnit(input, options?.embed !== false);
|
|
8813
8931
|
})),
|
|
8932
|
+
writeMany: (units, options) => run(Effect49.gen(function* () {
|
|
8933
|
+
if (!Array.isArray(units)) {
|
|
8934
|
+
return yield* new InputError({ message: "writeMany: units must be an array" });
|
|
8935
|
+
}
|
|
8936
|
+
for (const input of units) {
|
|
8937
|
+
const invalid = validateWriteInput(input);
|
|
8938
|
+
if (invalid !== undefined)
|
|
8939
|
+
return yield* invalid;
|
|
8940
|
+
}
|
|
8941
|
+
const mems = yield* core().store.writeMany(units);
|
|
8942
|
+
if (options?.embed !== false) {
|
|
8943
|
+
yield* core().store.ensureTopicTree(core().defaultModel.id);
|
|
8944
|
+
const vectors = yield* core().defaultEmbedder.embed(mems.map((m) => m.content));
|
|
8945
|
+
for (let i = 0;i < mems.length; i++) {
|
|
8946
|
+
yield* core().store.updateEmbedding(mems[i].id, [...vectors[i]], core().defaultModel.id, {
|
|
8947
|
+
scanCandidates: false
|
|
8948
|
+
});
|
|
8949
|
+
}
|
|
8950
|
+
return yield* Effect49.forEach(mems, (mem) => core().store.get(mem.id), {
|
|
8951
|
+
discard: false
|
|
8952
|
+
});
|
|
8953
|
+
}
|
|
8954
|
+
return mems;
|
|
8955
|
+
})),
|
|
8814
8956
|
get: (id) => run(resolveMemory(id)),
|
|
8815
8957
|
query: runQuery,
|
|
8816
8958
|
forget: (id) => run(resolveMemory(id).pipe(Effect49.flatMap((memory) => core().store.forget(memory.id)))),
|
|
@@ -9111,7 +9253,7 @@ var makeDimple = async (options) => {
|
|
|
9111
9253
|
let disposed = false;
|
|
9112
9254
|
let gate = Promise.resolve();
|
|
9113
9255
|
let releaseGate;
|
|
9114
|
-
let
|
|
9256
|
+
let inflight2 = 0;
|
|
9115
9257
|
let drainWaiters = [];
|
|
9116
9258
|
let reloadChain = Promise.resolve();
|
|
9117
9259
|
const run = (effect) => {
|
|
@@ -9123,12 +9265,12 @@ var makeDimple = async (options) => {
|
|
|
9123
9265
|
return Promise.reject(new StoreFailure({ message: "SDK disposed" }));
|
|
9124
9266
|
}
|
|
9125
9267
|
const stack = current;
|
|
9126
|
-
|
|
9268
|
+
inflight2 += 1;
|
|
9127
9269
|
return stack.runPromise(effect.pipe(Effect50.mapError(remapError))).catch((cause) => {
|
|
9128
9270
|
return Promise.reject(remapError(cause));
|
|
9129
9271
|
}).finally(() => {
|
|
9130
|
-
|
|
9131
|
-
if (
|
|
9272
|
+
inflight2 -= 1;
|
|
9273
|
+
if (inflight2 === 0 && drainWaiters.length > 0) {
|
|
9132
9274
|
const waiters = drainWaiters;
|
|
9133
9275
|
drainWaiters = [];
|
|
9134
9276
|
for (const wake of waiters)
|
|
@@ -9149,7 +9291,7 @@ var makeDimple = async (options) => {
|
|
|
9149
9291
|
release?.();
|
|
9150
9292
|
};
|
|
9151
9293
|
const drain = () => {
|
|
9152
|
-
if (
|
|
9294
|
+
if (inflight2 === 0)
|
|
9153
9295
|
return Promise.resolve();
|
|
9154
9296
|
return new Promise((resolve2) => {
|
|
9155
9297
|
drainWaiters.push(resolve2);
|
|
@@ -1,35 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* installs). When the config declares a `transformers` provider model,
|
|
7
|
-
* the intent is explicit — so the FIRST local-embed use installs the
|
|
8
|
-
* package automatically with the project's own package manager, instead
|
|
9
|
-
* of failing with a manual step.
|
|
10
|
-
*
|
|
11
|
-
* Safety notes:
|
|
12
|
-
* - The install is scoped to the detected project root (the manifest's
|
|
13
|
-
* directory) and uses the manager the project already uses.
|
|
14
|
-
* - EVERY install runs `--ignore-scripts` (npm/pnpm/yarn) or bun's
|
|
15
|
-
* default script-blocking — verified harmless: the onnxruntime-node
|
|
16
|
-
* binaries ship in the package; its postinstall is not required for
|
|
17
|
-
* embedding, and sharp is never loaded for text embeddings.
|
|
18
|
-
* - Failure (offline, restricted env) is a typed EmbeddingError with the
|
|
19
|
-
* manual command — never a crash.
|
|
20
|
-
*/
|
|
21
|
-
export type PackageManager = "bun" | "npm" | "pnpm" | "yarn";
|
|
22
|
-
export interface InstallTarget {
|
|
23
|
-
readonly pm: PackageManager;
|
|
24
|
-
readonly root: string;
|
|
25
|
-
}
|
|
26
|
-
/** Detect the project's package manager by walking UP from `cwd` for a
|
|
27
|
-
* lockfile (bun.lock/bun.lockb → bun, package-lock.json → npm,
|
|
28
|
-
* pnpm-lock.yaml → pnpm, yarn.lock → yarn). Falls back to bun + cwd. */
|
|
29
|
-
export declare const detectPackageManager: (cwd: string) => InstallTarget;
|
|
30
|
-
/** The install argv for a manager. `--ignore-scripts` on npm/pnpm/yarn:
|
|
31
|
-
* the blocked postinstalls are proven unnecessary for embedding (bun
|
|
32
|
-
* blocks them by default already). */
|
|
1
|
+
import { type PackageManager } from "./internal/installer/index.js";
|
|
2
|
+
export type { InstallTarget, PackageManager } from "./internal/installer/index.js";
|
|
3
|
+
export { detectPackageManager } from "./internal/installer/index.js";
|
|
4
|
+
/** The install argv for a manager with the transformers package (kept for
|
|
5
|
+
* callers/tests that assert the shape). */
|
|
33
6
|
export declare const installCommand: (pm: PackageManager) => readonly [string, ...string[]];
|
|
34
|
-
/**
|
|
7
|
+
/** Install transformers at the project root detected from `cwd`. The
|
|
8
|
+
* shared installer kills the child on timeout and dedups concurrent
|
|
9
|
+
* first-use installs; failures reject with the manual command. */
|
|
35
10
|
export declare const installTransformers: (cwd: string) => Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type PackageManager = "bun" | "npm" | "pnpm" | "yarn";
|
|
2
|
+
export interface InstallTarget {
|
|
3
|
+
readonly pm: PackageManager;
|
|
4
|
+
readonly root: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class InstallerError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/** Detect the project's package manager by walking UP from `cwd` for a
|
|
10
|
+
* lockfile. NO lockfile → npm (the most universal — a bun fallback broke
|
|
11
|
+
* Node-only machines with ENOENT). */
|
|
12
|
+
export declare const detectPackageManager: (cwd: string) => InstallTarget;
|
|
13
|
+
/** The executable name for a manager. win32 ships `.cmd` shims for
|
|
14
|
+
* npm/pnpm/yarn — the bare names fail with ENOENT. */
|
|
15
|
+
export declare const resolveExecutable: (pm: PackageManager, platform?: NodeJS.Platform) => string;
|
|
16
|
+
/** The base install argv for a manager. Scripts are skipped everywhere;
|
|
17
|
+
* pnpm at a workspace root additionally gets `-w`. */
|
|
18
|
+
export declare const installArgs: (pm: PackageManager, packages: readonly string[], root: string) => string[];
|
|
19
|
+
export interface InstallOptions {
|
|
20
|
+
readonly cwd?: string;
|
|
21
|
+
/** Hard deadline; the child is SIGKILLed when it trips (default 5 min). */
|
|
22
|
+
readonly timeoutMs?: number;
|
|
23
|
+
/** Injectable for tests (defaults to process.platform). */
|
|
24
|
+
readonly platform?: NodeJS.Platform;
|
|
25
|
+
}
|
|
26
|
+
export declare const installOptionalPeers: (packages: readonly string[], opts?: InstallOptions) => Promise<void>;
|
|
@@ -65,6 +65,11 @@ export interface ExplainResult {
|
|
|
65
65
|
}
|
|
66
66
|
export interface Interface {
|
|
67
67
|
readonly write: (input: WriteMemoryInput) => Effect.Effect<MemoryUnit, StoreError | DuplicateActiveKey>;
|
|
68
|
+
/** Bulk write: batch insert in ONE transaction (all-or-nothing; a
|
|
69
|
+
* duplicate logical key fails the whole batch with DuplicateActiveKey).
|
|
70
|
+
* The per-write graph candidate scan is NOT part of this — pair with
|
|
71
|
+
* `updateEmbedding(..., { scanCandidates: false })` for bulk ingest. */
|
|
72
|
+
readonly writeMany: (inputs: readonly WriteMemoryInput[]) => Effect.Effect<readonly MemoryUnit[], StoreError | DuplicateActiveKey>;
|
|
68
73
|
/** Upsert for the index path — the identity rule (same logical key ⇒
|
|
69
74
|
* confirmed `memory_supersedes`) made reachable for ACTIVE keys: demote
|
|
70
75
|
* the active holder of the key (if any), write the new unit, and link
|
|
@@ -139,7 +144,9 @@ export interface Interface {
|
|
|
139
144
|
readonly forget: (id: string) => Effect.Effect<void, NotFound | StoreError>;
|
|
140
145
|
readonly sweep: (namespace: string) => Effect.Effect<SweepResult, StoreError>;
|
|
141
146
|
readonly events: (memoryId: string) => Effect.Effect<readonly MemoryEvent[], StoreError>;
|
|
142
|
-
readonly updateEmbedding: (id: string, embedding: ReadonlyArray<number>, model: string
|
|
147
|
+
readonly updateEmbedding: (id: string, embedding: ReadonlyArray<number>, model: string, options?: {
|
|
148
|
+
readonly scanCandidates?: boolean;
|
|
149
|
+
}) => Effect.Effect<void, NotFound | StoreError>;
|
|
143
150
|
readonly findStale: (model: string) => Effect.Effect<readonly string[], StoreError>;
|
|
144
151
|
readonly createConcept: (name: string, embedding: ReadonlyArray<number>) => Effect.Effect<Concept, StoreError>;
|
|
145
152
|
readonly getConcept: (id: string) => Effect.Effect<Concept, NotFound | StoreError>;
|