@yugoo-design/dsh-design-knowledge-ladybug 0.1.0-beta.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,16 @@ The provider implements the complete `DesignKnowledgeService`. Delta and proposa
8
8
 
9
9
  Archive import accepts only an empty project namespace. It validates the neutral format, project, proposal digests, relation endpoints, terminal lifecycle decisions, and journal references before beginning a write. Import remaps source proposal revisions explicitly, rewrites decision proposal revisions to the target provider revisions, returns the complete source-to-target map, and commits the reconstruction in one database transaction.
10
10
 
11
+ The durable neutral snapshot lives in the Design Project at
12
+ `design_records/knowledge/archive.json`, tracked by project Git. DSH and Companion
13
+ restore it before exposing the provider and save it after settled knowledge
14
+ publication or decisions. `resetCache(projectId)` clears only this provider's
15
+ derived namespace under its existing serialization and transaction. It supports
16
+ restoring changed or older archive bytes and clearing a cache when the project
17
+ has no archive; it is not an old-state migration or permission to promote an
18
+ external database into a new project authority. Provider paths, locks, native
19
+ handles and embeddings remain local runtime data.
20
+
11
21
  The WASM package owns one worker for the Node.js process. Closing a project releases its database and project lock but intentionally keeps the worker available for reopen. Standalone embeddings may call `shutdownLadybugKnowledgeRuntime()` once, after all providers close, during terminal process teardown; opening after terminal shutdown fails loud. The reloadable DSH plugin drains every project handle on unload but leaves the process-global worker available for a later plugin activation.
12
22
 
13
23
  Configuration resolves once at open. Writer acquisition, project-lock retry, maximum query result, text byte, and filter cardinality budgets are explicit. Invalid queries fail before any database read. Lock or writer exhaustion fails without opening or mutating project state.
@@ -31,4 +41,28 @@ pnpm run build
31
41
  pnpm test
32
42
  ```
33
43
 
44
+ ### Local embedding resources
45
+
46
+ The default model is fixed to one repository revision. Its configuration,
47
+ tokenizer and quantized ONNX weights must match the recorded sizes and Git/LFS
48
+ hashes before a native inference child starts. Resource transfer has a separate
49
+ bounded preparation lifetime; the existing 180-second inference budget is unchanged.
50
+
51
+ Offline mode verifies the existing files and reports missing or incomplete
52
+ resources without network access. When download is permitted, interrupted
53
+ transfers remain in resumable staging files. Only a complete checksum-verified
54
+ file replaces the canonical cache entry; an invalid previous entry is retained.
55
+ Failure ends that maintenance pass. A later explicit maintenance request can
56
+ resume preparation; display/status reads do not retry it.
57
+ Companion's normal `serve --embedding-cache /absolute/cache` can select an
58
+ explicit prepared cache, including an isolated cache for installation checks.
59
+ Omitting it retains the existing per-user default location.
60
+
61
+ On macOS, explicit HTTP(S) proxy environment variables take precedence. Otherwise
62
+ the resource downloader and the model child use the existing manual system HTTPS
63
+ proxy and its bypass list. The parent environment, OS settings and TLS validation
64
+ are unchanged. PAC discovery and proxy authentication are not configured here.
65
+ Custom model/revision configurations retain their existing loader behavior; this
66
+ fixed asset inventory describes only the default model.
67
+
34
68
  DesignJournal remains the only human-decision authority. The `./dsh` host entry registers `ladybug-wasm` as the DSH composition's explicit default, derives each database root from external project runtime state, and exposes only `DesignKnowledgeService` to Core consumers. G/R/A scheduling, Studio, compaction, and future Codex/Claude integration remain outside this storage provider.
@@ -1,11 +1,11 @@
1
- import { DesignSemanticIndex } from "./semantic-index.js";
1
+ import { t as DesignSemanticIndex } from "./semantic-index-CigpMVTO.js";
2
2
  import { createRequire } from "node:module";
3
3
  import { createHash, randomInt, randomUUID } from "node:crypto";
4
4
  import { copyFile, lstat, mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
5
5
  import { constants, createReadStream } from "node:fs";
6
6
  import { dirname, isAbsolute, join, resolve } from "node:path";
7
7
  import { gunzipSync, gzipSync } from "node:zlib";
8
- import { DesignKnowledgeCheckpointConflictError, DesignKnowledgeManager, DesignKnowledgeMutationConflictError, DesignKnowledgeMutationId, DesignKnowledgeProposalDigest, DesignKnowledgeProviderId, DesignKnowledgeRevision, DesignKnowledgeRevisionConflictError, assertDesignKnowledgeDecisionAuthority, designKnowledgeProposalDigest, queryKnowledgeSnapshot } from "@yugoo-design/dsh-design-core";
8
+ import { DesignKnowledgeCheckpointConflictError, DesignKnowledgeManager, DesignKnowledgeMutationConflictError, DesignKnowledgeMutationId, DesignKnowledgeProposalDigest, DesignKnowledgeProviderId, DesignKnowledgeRevision, DesignKnowledgeRevisionConflictError, assertDesignKnowledgeDecisionAuthority, commitDesignProjectRecords, designKnowledgeProposalDigest, queryKnowledgeSnapshot, readDesignKnowledgePublication, restoreDesignKnowledgeRecords, saveDesignKnowledgeRecords } from "@yugoo-design/dsh-design-core";
9
9
  import lockfile from "proper-lockfile";
10
10
  //#region src/codec.ts
11
11
  const LIFECYCLES = /* @__PURE__ */ new Set([
@@ -1024,6 +1024,39 @@ var LadybugDesignKnowledgeProvider = class LadybugDesignKnowledgeProvider {
1024
1024
  });
1025
1025
  });
1026
1026
  }
1027
+ /**
1028
+ * Discard this project's derived cache before restoring its project archive.
1029
+ * Call only before exposing the provider to readers; the project files and
1030
+ * their Git history are never touched. Existing provider serialization and
1031
+ * project ownership cover reset plus the caller's subsequent import.
1032
+ */
1033
+ async resetCache(projectId) {
1034
+ return this.#serialized(async () => {
1035
+ this.#assertProject(projectId);
1036
+ await this.#fixed("CREATE NODE TABLE IF NOT EXISTS DesignSemanticCacheV1(key STRING, payload STRING, PRIMARY KEY(key));");
1037
+ await rm(this.#visibleSidecarPath(), { force: true });
1038
+ await this.#beginWrite();
1039
+ try {
1040
+ await this.#rows("MATCH ()-[v:KnowledgeRelationVersion]->() DELETE v;", {});
1041
+ for (const table of [
1042
+ "KnowledgeEntityVersion",
1043
+ "KnowledgeEntityIdentity",
1044
+ "KnowledgeMutation",
1045
+ "KnowledgeProposal",
1046
+ "KnowledgeDecision",
1047
+ "DesignSemanticCacheV1"
1048
+ ]) await this.#fixed(`MATCH (n:${table}) DELETE n;`);
1049
+ await this.#rows("MATCH (m:KnowledgeMeta {project: $project}) SET m.revision = $revision;", {
1050
+ project: this.#projectId,
1051
+ revision: this.#baseline
1052
+ });
1053
+ await this.#commit();
1054
+ } catch (error) {
1055
+ await this.#rollback().catch(() => {});
1056
+ throw error;
1057
+ }
1058
+ });
1059
+ }
1027
1060
  async importProject(archive) {
1028
1061
  return this.#serialized(async () => {
1029
1062
  this.#assertProject(archive.projectId);
@@ -1038,8 +1071,8 @@ var LadybugDesignKnowledgeProvider = class LadybugDesignKnowledgeProvider {
1038
1071
  this.#rows("MATCH ()-[v:KnowledgeRelationVersion]->() RETURN count(v) AS count;", {}),
1039
1072
  this.#rows("MATCH (m:KnowledgeMutation) RETURN count(m) AS count;", {})
1040
1073
  ]);
1041
- if (current !== 0 || occupied.some((rows) => asNumber(rows[0]?.count, "namespace record count") !== 0)) throw new Error("Ladybug Design Knowledge import requires an empty project namespace");
1042
- let target = 0;
1074
+ if (current !== this.#baseline || occupied.some((rows) => asNumber(rows[0]?.count, "namespace record count") !== 0)) throw new Error("Ladybug Design Knowledge import requires an empty project namespace");
1075
+ let target = current;
1043
1076
  const mapping = {};
1044
1077
  const proposalItems = [...validated.proposals].sort((left, right) => parseRevision(left.proposalRevision) - parseRevision(right.proposalRevision));
1045
1078
  const proposalEntityIds = new Set(proposalItems.flatMap((value) => value.proposal.entities.map((entity) => entity.id)));
@@ -1630,6 +1663,18 @@ function apply(ctx, config = {}) {
1630
1663
  recoveryStates.set(location.projectId, state);
1631
1664
  }
1632
1665
  });
1666
+ try {
1667
+ await restoreDesignKnowledgeRecords({
1668
+ projectRoot: await design.knowledgeProjectRoot(location.projectId),
1669
+ projectId: location.projectId,
1670
+ service: provider,
1671
+ cache: provider,
1672
+ resetCache: () => provider.resetCache(location.projectId)
1673
+ });
1674
+ } catch (error) {
1675
+ await provider.close();
1676
+ throw error;
1677
+ }
1633
1678
  providers.set(location.projectId, provider);
1634
1679
  return Object.freeze({
1635
1680
  projectId: location.projectId,
@@ -1644,6 +1689,23 @@ function apply(ctx, config = {}) {
1644
1689
  });
1645
1690
  } });
1646
1691
  const unregister = design.registerDesignKnowledgeRuntime({
1692
+ persistProjectRecords: async (projectId) => {
1693
+ const provider = providers.get(projectId);
1694
+ if (!provider) throw new Error("Knowledge project cache is not open");
1695
+ const projectRoot = await design.knowledgeProjectRoot(projectId);
1696
+ await saveDesignKnowledgeRecords({
1697
+ projectRoot,
1698
+ projectId,
1699
+ service: provider,
1700
+ cache: provider,
1701
+ publication: await readDesignKnowledgePublication(provider),
1702
+ persist: (paths) => commitDesignProjectRecords({
1703
+ projectRoot,
1704
+ paths,
1705
+ message: "design(knowledge): save project records"
1706
+ })
1707
+ });
1708
+ },
1647
1709
  recoveryStatus: (projectId) => recoveryStates.get(projectId),
1648
1710
  retryRecovery: (projectId) => {
1649
1711
  retries.add(projectId);
@@ -1652,6 +1714,7 @@ function apply(ctx, config = {}) {
1652
1714
  administration: manager,
1653
1715
  service: manager.service,
1654
1716
  semantic: {
1717
+ enabled: semantic.config.provider !== "disabled",
1655
1718
  identity: semantic.publicationIdentity,
1656
1719
  publicationCache: (projectId) => providers.get(projectId),
1657
1720
  read: async (projectId, graph) => {
package/lib/dsh-plugin.js CHANGED
@@ -1,3 +1,2 @@
1
- import { i as name, n as apply, r as inject, t as LADYBUG_WASM_PROVIDER_ID } from "./dsh-plugin-C0TG9tJI.js";
2
- import "./semantic-index.js";
1
+ import { i as name, n as apply, r as inject, t as LADYBUG_WASM_PROVIDER_ID } from "./dsh-plugin-BWO-rNYF.js";
3
2
  export { LADYBUG_WASM_PROVIDER_ID, apply, inject, name };
@@ -0,0 +1,104 @@
1
+ //#region src/embedding-diagnostic.ts
2
+ /** Private child IPC vocabulary. Never serialize an exception or its free text. */
3
+ const names = /* @__PURE__ */ new Set([
4
+ "Error",
5
+ "TypeError",
6
+ "SyntaxError",
7
+ "RangeError",
8
+ "ReferenceError",
9
+ "URIError",
10
+ "EvalError",
11
+ "AggregateError"
12
+ ]);
13
+ const codes = /* @__PURE__ */ new Set([
14
+ "ENOTFOUND",
15
+ "EAI_AGAIN",
16
+ "ECONNREFUSED",
17
+ "ECONNRESET",
18
+ "ETIMEDOUT",
19
+ "UND_ERR_CONNECT_TIMEOUT",
20
+ "UND_ERR_HEADERS_TIMEOUT",
21
+ "UND_ERR_BODY_TIMEOUT",
22
+ "CERT_HAS_EXPIRED",
23
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
24
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
25
+ "SELF_SIGNED_CERT_IN_CHAIN",
26
+ "ERR_TLS_CERT_ALTNAME_INVALID",
27
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
28
+ "ERR_MODULE_NOT_FOUND",
29
+ "MODULE_NOT_FOUND",
30
+ "ERR_DLOPEN_FAILED",
31
+ "EACCES",
32
+ "EPERM",
33
+ "ENOENT",
34
+ "ENOSPC"
35
+ ]);
36
+ const hubPrefixes = [
37
+ [400, "Bad request error occurred while trying to load file"],
38
+ [401, "Unauthorized access to file"],
39
+ [403, "Forbidden access to file"],
40
+ [404, "Could not locate file"],
41
+ [408, "Request timeout error occurred while trying to load file"],
42
+ [500, "Internal server error error occurred while trying to load file"],
43
+ [502, "Bad gateway error occurred while trying to load file"],
44
+ [503, "Service unavailable error occurred while trying to load file"],
45
+ [504, "Gateway timeout error occurred while trying to load file"]
46
+ ];
47
+ const hubTokens = new Set(hubPrefixes.map(([status]) => `hf3.8.1-http-${status}`));
48
+ /** Revalidate at the receiving boundary; unknown keys and values never escape. */
49
+ function readEmbeddingDiagnostic(value) {
50
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
51
+ const result = {}, input = value;
52
+ try {
53
+ for (const [key, allowed] of [
54
+ ["name", names],
55
+ ["code", codes],
56
+ ["causeCode", codes],
57
+ ["hubText", hubTokens]
58
+ ]) {
59
+ if (!Object.hasOwn(input, key)) continue;
60
+ const item = input[key];
61
+ if (typeof item === "string" && allowed.has(item)) result[key] = item;
62
+ }
63
+ for (const key of ["localOnly", "remoteAllowed"]) {
64
+ if (!Object.hasOwn(input, key)) continue;
65
+ const item = input[key];
66
+ if (typeof item === "boolean") result[key] = item;
67
+ }
68
+ } catch {}
69
+ return Object.keys(result).length ? result : void 0;
70
+ }
71
+ function captureEmbeddingDiagnostic(error, phase, effective) {
72
+ const result = {}, policy = readEmbeddingDiagnostic(effective);
73
+ if (policy?.localOnly !== void 0) result.localOnly = policy.localOnly;
74
+ if (policy?.remoteAllowed !== void 0) result.remoteAllowed = policy.remoteAllowed;
75
+ try {
76
+ const seen = /* @__PURE__ */ new Set();
77
+ let current = error;
78
+ for (let depth = 0; depth < 3 && current && typeof current === "object" && !seen.has(current); depth++) {
79
+ seen.add(current);
80
+ const item = current;
81
+ const name = item.name;
82
+ if (!depth && typeof name === "string" && names.has(name)) result.name = name;
83
+ const code = item.code;
84
+ if (typeof code === "string" && codes.has(code)) {
85
+ if (!depth) result.code = code;
86
+ else result.causeCode ??= code;
87
+ }
88
+ const message = item.message;
89
+ if (!depth && phase === "model" && typeof message === "string" && message.endsWith("\".")) {
90
+ const matched = hubPrefixes.find(([, prefix]) => message.startsWith(`${prefix}: "`));
91
+ if (matched) result.hubText = `hf3.8.1-http-${matched[0]}`;
92
+ }
93
+ current = item.cause;
94
+ }
95
+ } catch {}
96
+ return readEmbeddingDiagnostic(result);
97
+ }
98
+ /** Closed values keep the suffix below 256 characters, including every field. */
99
+ function embeddingDiagnosticSuffix(value) {
100
+ const diagnostic = readEmbeddingDiagnostic(value);
101
+ return diagnostic ? ` [embedding: ${Object.entries(diagnostic).map(([key, item]) => `${key}=${item}`).join("; ")}]` : "";
102
+ }
103
+ //#endregion
104
+ export { embeddingDiagnosticSuffix as n, readEmbeddingDiagnostic as r, captureEmbeddingDiagnostic as t };
@@ -1,3 +1,4 @@
1
+ import { t as captureEmbeddingDiagnostic } from "./embedding-diagnostic-CvIl3TWJ.js";
1
2
  //#region src/embedding-text.mjs
2
3
  /** Exact tokenizer windows. Preserve every character; never silently truncate evidence. */
3
4
  function embeddingWindows(text, prefix, tokenizer, limit = 512, maxParts = 32) {
@@ -33,6 +34,7 @@ let extractor;
33
34
  process.on("disconnect", () => process.exit(0));
34
35
  process.on("message", async ({ texts, config }) => {
35
36
  let phase = "runtime";
37
+ let effective;
36
38
  try {
37
39
  const { pipeline, env } = await import("@huggingface/transformers");
38
40
  if (process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.https_proxy || process.env.http_proxy) {
@@ -43,6 +45,10 @@ process.on("message", async ({ texts, config }) => {
43
45
  phase = "model";
44
46
  env.cacheDir = config.cacheDir;
45
47
  env.allowRemoteModels = !config.localOnly;
48
+ effective = {
49
+ localOnly: config.localOnly,
50
+ remoteAllowed: env.allowRemoteModels
51
+ };
46
52
  extractor ??= await pipeline("feature-extraction", config.model, {
47
53
  revision: config.revision,
48
54
  dtype: "q8",
@@ -72,8 +78,12 @@ process.on("message", async ({ texts, config }) => {
72
78
  offset += group.length;
73
79
  return result;
74
80
  }) });
75
- } catch {
76
- process.send?.({ error: phase });
81
+ } catch (error) {
82
+ const diagnostic = captureEmbeddingDiagnostic(error, phase, effective);
83
+ process.send?.({
84
+ error: phase,
85
+ ...diagnostic ? { diagnostic } : {}
86
+ });
77
87
  }
78
88
  });
79
89
  //#endregion
package/lib/index.d.ts CHANGED
@@ -57,6 +57,13 @@ declare class LadybugDesignKnowledgeProvider implements LadybugDesignKnowledgeSe
57
57
  proposeBatch(batch: DesignKnowledgeProposalBatch): Promise<DesignKnowledgeProposalBatchResult>;
58
58
  applyDecision(decision: DesignKnowledgeDecision, pin: Parameters<DesignKnowledgeService['applyDecision']>[1]): Promise<DesignKnowledgeMutationResult>;
59
59
  exportProject(projectId: DesignProjectId, requested?: DesignKnowledgeRevision): Promise<DesignKnowledgeArchive>;
60
+ /**
61
+ * Discard this project's derived cache before restoring its project archive.
62
+ * Call only before exposing the provider to readers; the project files and
63
+ * their Git history are never touched. Existing provider serialization and
64
+ * project ownership cover reset plus the caller's subsequent import.
65
+ */
66
+ resetCache(projectId: DesignProjectId): Promise<void>;
60
67
  importProject(archive: DesignKnowledgeArchive): Promise<DesignKnowledgeImportReport>;
61
68
  }
62
69
  //#endregion
package/lib/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { a as LADYBUG_KNOWLEDGE_SCHEMA_VERSION, c as LadybugSchemaMismatchError, d as shutdownLadybugKnowledgeRuntime, f as resolveLadybugKnowledgeConfig, i as name, l as LadybugWriterBusyError, n as apply, o as LadybugDesignKnowledgeProvider, r as inject, s as LadybugProjectLockError, t as LADYBUG_WASM_PROVIDER_ID, u as isRecoverableLadybugError } from "./dsh-plugin-C0TG9tJI.js";
2
- import { DesignSemanticIndex } from "./semantic-index.js";
1
+ import { a as LADYBUG_KNOWLEDGE_SCHEMA_VERSION, c as LadybugSchemaMismatchError, d as shutdownLadybugKnowledgeRuntime, f as resolveLadybugKnowledgeConfig, i as name, l as LadybugWriterBusyError, n as apply, o as LadybugDesignKnowledgeProvider, r as inject, s as LadybugProjectLockError, t as LADYBUG_WASM_PROVIDER_ID, u as isRecoverableLadybugError } from "./dsh-plugin-BWO-rNYF.js";
2
+ import { t as DesignSemanticIndex } from "./semantic-index-CigpMVTO.js";
3
3
  export { DesignSemanticIndex, LADYBUG_KNOWLEDGE_SCHEMA_VERSION, LADYBUG_WASM_PROVIDER_ID, LadybugDesignKnowledgeProvider, LadybugProjectLockError, LadybugSchemaMismatchError, LadybugWriterBusyError, apply as applyDshLadybugKnowledge, inject as dshLadybugKnowledgeInject, name as dshLadybugKnowledgeName, isRecoverableLadybugError, resolveLadybugKnowledgeConfig, shutdownLadybugKnowledgeRuntime };