dsh-plugin-dbgraph 0.0.0-stage → 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.
Files changed (3) hide show
  1. package/README.md +178 -2
  2. package/cordis.patch.yml +29 -0
  3. package/package.json +48 -3
package/README.md CHANGED
@@ -1,3 +1,179 @@
1
- # Temporary Holding Version
1
+ # dsh-plugin-dbgraph
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Database structure the agent already knows, so it answers with one tool call instead of five.**
4
+
5
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that indexes a remote
6
+ database's *catalog* — every table, column, comment, key, index, and a bounded sample of enum
7
+ literals — into a local SQLite index under `~/.dsh/dbgraph`. The model reads that index (and a digest
8
+ of it placed directly in the system prompt) instead of spending round-trips on `SHOW TABLES` and
9
+ `DESCRIBE`, then fetches the actual values with exactly one network call.
10
+
11
+ ## The problem it removes
12
+
13
+ Without an index, answering *"what is 张三's password?"* looks like this:
14
+
15
+ ```
16
+ 1. SHOW TABLES ← network round-trip
17
+ 2. DESCRIBE sys_user ← network round-trip
18
+ 3. SELECT ... WHERE status = 1 ← wrong: status is a varchar
19
+ 4. SELECT ... WHERE status = 'paid' ← guess corrected
20
+ 5. 2,000 rows into the transcript ← context burned
21
+ ```
22
+
23
+ Four model round-trips, four network round-trips, two wrong guesses. **The expensive part is not the
24
+ database — it is the model's turns.** Each one costs seconds; a 100 ms query does not.
25
+
26
+ With dbgraph:
27
+
28
+ ```
29
+ 1. dbgraph find {entity:'用户', fields:['密码'], where:{name:'张三'}}
30
+ ← one row, and the answer says which table and column it used
31
+ ```
32
+
33
+ ## Structure local, data online
34
+
35
+ | | Where | Why |
36
+ |---|---|---|
37
+ | Tables, columns, comments, keys, indexes, enum samples, row estimates | **Local index** | Zero network, usable offline, no credential needed to ask what a column means |
38
+ | Actual row values | **Always the server** | A cached password is not a fast password, it is a wrong one |
39
+
40
+ Nothing derived from business data is persisted. The one data-shaped field is `enum_values_json`, a
41
+ `SELECT DISTINCT … LIMIT n` sample that teaches the model whether `status` holds `'paid'` or `1`.
42
+
43
+ ## Layout
44
+
45
+ ```
46
+ packages/
47
+ bundle cordis.patch.yml — mounts everything as one profile layer
48
+ service ctx.dbgraph: the vocabulary, the dialect registry, the index protocol
49
+ catalog one DatabaseDialect per engine (SQLite, MySQL; PostgreSQL/MongoDB/Redis to follow)
50
+ store ~/.dsh/dbgraph: connections.json, the index writer, the reader, the resolver
51
+ tool the two model tools, /dbgraph, and the prompt digest
52
+ crates/
53
+ dbgraph-indexer the index write path in Rust (normalize + terms + SQLite)
54
+ ```
55
+
56
+ ### Why the write path is Rust
57
+
58
+ `node:sqlite` is a synchronous API. Writing ten thousand columns through it blocks the event loop for
59
+ as long as the write takes — this is the same wall `dsh-plugin-codegraph` and
60
+ `dsh-plugin-sessiongraph` hit, and both moved their kernels behind napi `AsyncTask` to get past it.
61
+ `crates/dbgraph-indexer` does the same: normalization, term generation, and the SQLite write run on
62
+ libuv's thread pool, so a rebuild never stalls the host.
63
+
64
+ Measured on one machine, same catalog, same fixed clock — 500 tables / 10,000 columns:
65
+
66
+ | writer | write | index file | longest event-loop block |
67
+ |---|---|---|---|
68
+ | JavaScript | 1992 ms | 8.46 MiB | **1992 ms** (the whole write) |
69
+ | Rust (`AsyncTask`) | 218 ms | 8.41 MiB | **33.6 ms** |
70
+
71
+ **9.1× faster, and the blocking cost drops 59×** — which is the number that matters, because a
72
+ two-second stall is two seconds of the host doing nothing else.
73
+
74
+ The two writers are checked against each other rather than trusted: given the same catalog and the
75
+ same fixed `now`, `objects`, `columns`, `edges`, `terms`, and `search` (rowids included) must match
76
+ row for row, and only `meta.writer` may differ. Fixture expectations are generated by *running* the
77
+ JavaScript writer, never hand-written, so a change on either side that is not mirrored fails the
78
+ build.
79
+
80
+ The JavaScript writer is therefore not an optimization fallback — it is the **parity reference** and
81
+ the **degradation path** (no Rust toolchain, no addon, a throwing call, an unparsable answer all fall
82
+ back to it, once and quietly).
83
+
84
+ ### Two things that had to be measured, not assumed
85
+
86
+ - **`node:sqlite` contentless FTS5 stores nothing you can read back.** Every column — `UNINDEXED`
87
+ ones included — returns `NULL`; only `rowid` and `rank` survive. The index therefore uses a
88
+ *contentful* table, where `object_id UNINDEXED` reads back verbatim. (The FTS5 column grammar is
89
+ also `column-name [UNINDEXED]`: a type name in front is a parse error.)
90
+ - **`unicode61` treats a run of Chinese as one token.** Store `广告点击` verbatim and `MATCH '广告'`
91
+ finds nothing. So `text` holds a pre-tokenized stream — camel/digit boundaries for ASCII, bigrams
92
+ for CJK — and queries go through the same tokenizer, joined with `OR` (a space means *phrase*, not
93
+ *any of these*).
94
+
95
+ ### Picking a writer
96
+
97
+ `dbgraph_index` takes `writer: 'auto' | 'js' | 'rust'`:
98
+
99
+ - `js` — the JavaScript writer, always. Never touches the addon.
100
+ - `rust` — the native writer only; a missing artifact or a throwing call fails the run rather than
101
+ quietly producing a slower index.
102
+ - `auto` (default) — native when available; a missing artifact, a rejected call, or an unreadable
103
+ report logs **once per process** and falls back to JavaScript.
104
+
105
+ `DSH_DBGRAPH_RUST_ADDON` points at an artifact explicitly: a test hook for the fallback path, and an
106
+ escape hatch for a deployment that keeps the addon outside `crates/`. It defaults to
107
+ `crates/dbgraph-indexer/artifacts/dbgraph-indexer.<platform>-<arch>.node`.
108
+
109
+ One acknowledged difference: the JavaScript writer counts dangling edges (an edge naming a table the
110
+ snapshot does not contain) into `report.warnings`; the native writer drops them silently. The stored
111
+ data is identical — only that one warning line differs.
112
+
113
+ ## Index format
114
+
115
+ One SQLite file per connection, replaced wholesale on every run (write to a staging file named
116
+ `<index>.<pid>-<nonce>.tmp`, then rename). The nonce is per run, so two runs never share the staging
117
+ file. That single decision removes incremental diffing, deletes, and half-updated indexes.
118
+
119
+ ```
120
+ meta key/value: engine, database, schemaVersion, l1IndexedAt, l2IndexedAt
121
+ objects one row per table / view / collection / key pattern
122
+ columns ordinal, type, type_class, nullability, default, comment, pk/unique, enum sample
123
+ edges fk | index | view_ref | code_ref
124
+ terms normalized words + Chinese bigrams + alias expansion, for semantic lookup
125
+ search FTS5 (contentless) over object and column names and comments
126
+ ```
127
+
128
+ `status.json` beside the indexes carries a digest the system-prompt section reads **synchronously** —
129
+ a section is assembled on every model request, so opening SQLite there would be a per-step cost the
130
+ plugin refuses to pay.
131
+
132
+ ## Two levels
133
+
134
+ | Level | Content | Where |
135
+ |---|---|---|
136
+ | **L1** | object and column names, declared types | inline, seconds — usable immediately |
137
+ | **L2** | comments, foreign keys, indexes, row estimates, enum samples | background job |
138
+
139
+ An L1 failure to enrich never discards the structural snapshot: an index that answers "which columns
140
+ exist" is most of the value.
141
+
142
+ ## Tools
143
+
144
+ | Tool | Timeout | What it does |
145
+ |---|---|---|
146
+ | `dbgraph` | 30 s | `connections` `schema` `objects` `columns` `find` `sql` `impact` `refs` |
147
+ | `dbgraph_index` | 300 s | Builds or refreshes one connection (`l1` / `l2` / `full`); hands a large run to a background job |
148
+
149
+ `find` is the one that matters: it resolves words to a table and columns **offline**, then runs one
150
+ parameterized `SELECT`. It answers `ambiguous` with ranked candidates rather than guessing, and
151
+ `missing_predicate` rather than scanning a table because the user said "this user" without saying
152
+ which.
153
+
154
+ ## Masking
155
+
156
+ Sensitive columns (`password`, `passwd`, `pwd`, `secret`, `token`, `salt`, `id_card`, …) come back as
157
+ `[REDACTED]` by default. Without that, one careless question puts a password into a transcript — and
158
+ this harness persists transcripts and indexes them. `sensitiveColumns: 'show'` per connection turns it
159
+ off.
160
+
161
+ Credentials never live in `connections.json`: a connection stores a **credential reference**, and the
162
+ password is resolved through the host's `ctx.credentials` seam on each operation.
163
+
164
+ ## Commands
165
+
166
+ ```sh
167
+ pnpm install
168
+ pnpm run build # tsc -b across the four TS packages
169
+ pnpm test # vitest run
170
+ pnpm run test:coverage # per-file 100% thresholds
171
+ pnpm run rust:build # cargo build --release for crates/dbgraph-indexer
172
+ ```
173
+
174
+ Tests resolve to live `src/`, never to built `lib/` (`tsconfig.base.json`'s `paths` map is the
175
+ facade), so a stale build can never load a second copy of a module singleton.
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1,29 @@
1
+ # The dsh-plugin-dbgraph bundle patch: mounts the whole database-structure capability
2
+ # as ONE layer over an existing dsh profile.
3
+ #
4
+ # Row order carries no load semantics — activation is service-availability driven — so the
5
+ # seam may appear before or after the providers that fill it. The user's own profile patch
6
+ # layer can address any row below by `id` to retune it (last write per row wins), e.g.
7
+ # disabling a dialect's `engines` list or lowering the tool's `maxRows`.
8
+
9
+ - insert:
10
+ # Service Definition: publishes ctx.dbgraph — the dialect registry, the single index-store
11
+ # reservation, and the nine normalized operations. Holds no store and reaches no server.
12
+ - id: dbgraph
13
+ name: 'dsh-plugin-dbgraph-service'
14
+
15
+ # Service Provider (dialects): one DatabaseDialect per engine. Each reaches one server family
16
+ # for a probe, an L1 snapshot, an L2 enrichment, and one execute. Registers dialects only.
17
+ - id: dbgraph-catalog
18
+ name: 'dsh-plugin-dbgraph-catalog'
19
+
20
+ # Service Provider (offline): the on-disk index under ~/.dsh/dbgraph — reader, JavaScript
21
+ # write path (the parity reference and the fallback), and the prompt-digest sidecar.
22
+ - id: dbgraph-store
23
+ name: 'dsh-plugin-dbgraph-store'
24
+
25
+ # Consumer: the model-facing `dbgraph` and `dbgraph_index` tools, their system-prompt
26
+ # section (the schema digest that removes the discovery round-trips), and the `/dbgraph`
27
+ # command.
28
+ - id: dbgraph-tool
29
+ name: 'dsh-plugin-dbgraph-tool'
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "dsh-plugin-dbgraph",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Database structure for DeepSeek Harness — indexes a remote database catalog into a local index so the agent knows every table and column before it writes a query, then answers with one tool call",
5
+ "keywords": [
6
+ "dsh",
7
+ "dsh-plugin",
8
+ "deepseek-harness",
9
+ "cordis",
10
+ "cordis-plugin",
11
+ "database",
12
+ "sql",
13
+ "mysql",
14
+ "postgresql",
15
+ "sqlite",
16
+ "mongodb",
17
+ "redis",
18
+ "schema",
19
+ "ai-agent",
20
+ "llm-tools",
21
+ "agent-tools"
22
+ ],
23
+ "license": "MIT",
24
+ "author": "CC ZHAO",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/CC19990113/dsh-plugin-dbgraph.git",
28
+ "directory": "packages/bundle"
29
+ },
30
+ "homepage": "https://github.com/CC19990113/dsh-plugin-dbgraph#readme",
31
+ "bugs": "https://github.com/CC19990113/dsh-plugin-dbgraph/issues",
32
+ "type": "module",
33
+ "files": [
34
+ "cordis.patch.yml"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "provenance": true
39
+ },
40
+ "dsh": {
41
+ "bundle": {
42
+ "patch": "./cordis.patch.yml"
43
+ }
44
+ },
45
+ "dependencies": {
46
+ "dsh-plugin-dbgraph-tool": "^0.1.0",
47
+ "dsh-plugin-dbgraph-store": "^0.1.0",
48
+ "dsh-plugin-dbgraph-catalog": "^0.1.0",
49
+ "dsh-plugin-dbgraph-service": "^0.1.0"
50
+ }
6
51
  }