skillscript-runtime 0.13.8 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/dist/bootstrap.d.ts +13 -13
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +31 -31
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/compile.js +2 -2
- package/dist/compile.js.map +1 -1
- package/dist/connectors/config.d.ts +4 -4
- package/dist/connectors/config.d.ts.map +1 -1
- package/dist/connectors/config.js +7 -7
- package/dist/connectors/config.js.map +1 -1
- package/dist/connectors/data-store-mcp.d.ts +29 -0
- package/dist/connectors/data-store-mcp.d.ts.map +1 -0
- package/dist/connectors/{memory-store-mcp.js → data-store-mcp.js} +56 -31
- package/dist/connectors/data-store-mcp.js.map +1 -0
- package/dist/connectors/{memory-store.d.ts → data-store.d.ts} +12 -12
- package/dist/connectors/data-store.d.ts.map +1 -0
- package/dist/connectors/{memory-store.js → data-store.js} +7 -7
- package/dist/connectors/data-store.js.map +1 -0
- package/dist/connectors/filter-enforcement.d.ts +18 -0
- package/dist/connectors/filter-enforcement.d.ts.map +1 -0
- package/dist/connectors/filter-enforcement.js +57 -0
- package/dist/connectors/filter-enforcement.js.map +1 -0
- package/dist/connectors/index.d.ts +5 -5
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js +7 -7
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/local-model-mcp.js +1 -1
- package/dist/connectors/local-model-mcp.js.map +1 -1
- package/dist/connectors/registry.d.ts +10 -10
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +11 -11
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +1 -1
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/connectors/types.d.ts +36 -23
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js +2 -2
- package/dist/connectors/types.js.map +1 -1
- package/dist/dashboard/spa/app.js +2 -2
- package/dist/errors.d.ts +43 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +70 -4
- package/dist/errors.js.map +1 -1
- package/dist/help-content.js +27 -27
- package/dist/help-content.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +48 -56
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +19 -16
- package/dist/mcp-server.js.map +1 -1
- package/dist/mutation-gate.d.ts +61 -0
- package/dist/mutation-gate.d.ts.map +1 -0
- package/dist/mutation-gate.js +89 -0
- package/dist/mutation-gate.js.map +1 -0
- package/dist/runtime-config.d.ts +2 -2
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +1 -1
- package/dist/runtime-config.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +59 -17
- package/dist/runtime.js.map +1 -1
- package/dist/skill-manager.js +1 -1
- package/dist/skill-manager.js.map +1 -1
- package/dist/testing/conformance.d.ts +8 -8
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +8 -8
- package/dist/testing/conformance.js.map +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/trace.d.ts +3 -3
- package/docs/adopter-playbook.md +85 -41
- package/docs/configuration.md +8 -8
- package/docs/connector-contract-reference.md +9 -9
- package/docs/language-reference.md +38 -63
- package/docs/sqlite-skill-store.md +2 -2
- package/examples/README.md +5 -5
- package/examples/connectors/{MemoryStoreTemplate/MemoryStoreTemplate.ts → DataStoreTemplate/DataStoreTemplate.ts} +56 -43
- package/examples/connectors/{MemoryStoreTemplate → DataStoreTemplate}/README.md +39 -39
- package/examples/connectors/LocalModelTemplate/README.md +1 -1
- package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +2 -2
- package/examples/connectors/McpConnectorTemplate/README.md +5 -5
- package/examples/connectors/README.md +3 -3
- package/examples/connectors/SkillStoreTemplate/README.md +1 -1
- package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +1 -1
- package/examples/custom-bootstrap.example.ts +15 -8
- package/examples/onboarding-scaffold/README.md +9 -10
- package/examples/onboarding-scaffold/bootstrap.ts +18 -13
- package/examples/onboarding-scaffold/connectors.json +1 -1
- package/examples/onboarding-scaffold/{file-memory-store.ts → file-data-store.ts} +34 -23
- package/examples/programmatic-trace-demo.mjs +1 -1
- package/examples/skillscripts/classify-support-ticket.skill.md +9 -8
- package/examples/skillscripts/cut-release-tag.skill.md +6 -6
- package/examples/skillscripts/doc-qa-with-citations.skill.md +3 -3
- package/examples/skillscripts/feedback-sentiment-scan.skill.md +9 -8
- package/examples/skillscripts/hello.skill.provenance.json +1 -1
- package/examples/skillscripts/morning-brief.skill.md +3 -3
- package/examples/skillscripts/service-health-watch.skill.md +5 -4
- package/package.json +1 -1
- package/scaffold/config.toml +6 -4
- package/scaffold/connectors.json +3 -3
- package/dist/connectors/memory-store-mcp.d.ts +0 -20
- package/dist/connectors/memory-store-mcp.d.ts.map +0 -1
- package/dist/connectors/memory-store-mcp.js.map +0 -1
- package/dist/connectors/memory-store.d.ts.map +0 -1
- package/dist/connectors/memory-store.js.map +0 -1
- /package/examples/onboarding-scaffold/{memory.example.json → data.example.json} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DataStoreTemplate — fork-me skeleton
|
|
2
2
|
|
|
3
|
-
A skeleton `
|
|
3
|
+
A skeleton `DataStore` implementation for adopters writing their own. Not runnable; every method throws a `TODO` error. Copy this directory, rename, fill in the substrate-specific work.
|
|
4
4
|
|
|
5
|
-
Use this when you want skillscript
|
|
5
|
+
Use this when you want skillscript data persistence backed by:
|
|
6
6
|
- A vector database (Pinecone, Weaviate, Qdrant, Chroma)
|
|
7
|
-
- A memory
|
|
7
|
+
- A data store backing (memory broker like AMP, hosted memory API, vector DB, etc.)
|
|
8
8
|
- A different SQL flavor (Postgres + pgvector, MySQL)
|
|
9
9
|
- An HTTP CRUD service
|
|
10
10
|
- Anything else with query + write semantics
|
|
@@ -12,32 +12,32 @@ Use this when you want skillscript memories backed by:
|
|
|
12
12
|
## The three legs
|
|
13
13
|
|
|
14
14
|
```
|
|
15
|
-
|
|
16
|
-
├──
|
|
15
|
+
DataStore (choose which connector)
|
|
16
|
+
├── SqliteDataStore (bundled, SQLite + FTS5 — src/connectors/data-store.ts)
|
|
17
17
|
├── (no other bundled impl — vector DBs and remote services are adopter-side)
|
|
18
18
|
└── Your fork from this template
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
The runtime is substrate-agnostic.
|
|
21
|
+
The runtime is substrate-agnostic. Data records don't know which backend they're stored against — the contract is the `DataStore` interface, and any class implementing it works.
|
|
22
22
|
|
|
23
23
|
## Forking workflow
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
cp -r examples/connectors/
|
|
26
|
+
cp -r examples/connectors/DataStoreTemplate examples/connectors/MyDataStore
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
1. **Rename the class.** Convention: `<Substrate>
|
|
30
|
-
2. **Define your config interface.** Edit `
|
|
29
|
+
1. **Rename the class.** Convention: `<Substrate>DataStore` (e.g., `PineconeDataStore`, `PostgresDataStore`, `AmpDataStore`).
|
|
30
|
+
2. **Define your config interface.** Edit `DataStoreTemplateConfig` to declare what your substrate needs (connection URL, API key, vault name, index name, etc.).
|
|
31
31
|
3. **Implement each method.** Three methods + `staticCapabilities()`. Each has a TODO comment in the skeleton explaining what to do.
|
|
32
32
|
4. **Update `staticCapabilities()`** to declare what your impl actually supports. The runtime + lint consult these flags before exercising features.
|
|
33
33
|
5. **Wire from your adopter bootstrap:**
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { Registry } from "skillscript-runtime";
|
|
37
|
-
import {
|
|
37
|
+
import { MyDataStore } from "./MyDataStore.js";
|
|
38
38
|
|
|
39
39
|
const registry = new Registry();
|
|
40
|
-
registry.
|
|
40
|
+
registry.registerDataStore("primary", new MyDataStore({
|
|
41
41
|
// your config
|
|
42
42
|
}));
|
|
43
43
|
```
|
|
@@ -46,37 +46,37 @@ cp -r examples/connectors/MemoryStoreTemplate examples/connectors/MyMemoryStore
|
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
48
|
import { describe, it } from "vitest";
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
49
|
+
import { DataStoreConformance } from "skillscript-runtime/testing";
|
|
50
|
+
import { MyDataStore } from "./MyDataStore.js";
|
|
51
51
|
|
|
52
|
-
describe("
|
|
53
|
-
const tests =
|
|
54
|
-
build: () => new
|
|
55
|
-
ctor:
|
|
52
|
+
describe("MyDataStore conformance", () => {
|
|
53
|
+
const tests = DataStoreConformance.buildTests({
|
|
54
|
+
build: () => new MyDataStore({ /* test config */ }),
|
|
55
|
+
ctor: MyDataStore,
|
|
56
56
|
});
|
|
57
57
|
for (const t of tests) it(`[${t.category}] ${t.name}`, t.run);
|
|
58
58
|
});
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
The conformance suite verifies your impl honors the contract: method presence, return-type shape, capability flag self-consistency. Passes → runtime treats your impl interchangeably with `
|
|
61
|
+
The conformance suite verifies your impl honors the contract: method presence, return-type shape, capability flag self-consistency. Passes → runtime treats your impl interchangeably with `SqliteDataStore`.
|
|
62
62
|
|
|
63
63
|
## Reference implementation
|
|
64
64
|
|
|
65
65
|
When in doubt about semantics, read the bundled impl:
|
|
66
66
|
|
|
67
|
-
- **`src/connectors/
|
|
67
|
+
- **`src/connectors/data-store.ts`** — `SqliteDataStore`, SQLite + FTS5-backed. Schema lives in `bootstrap()` (memories table + FTS virtual table + AFTER INSERT/UPDATE/DELETE triggers). Single-process. Capability flags: `supports_writes: true`, `supports_tag_filter: true`, FTS only (semantic / rerank false).
|
|
68
68
|
|
|
69
69
|
## Contract surface (3 methods)
|
|
70
70
|
|
|
71
|
-
The
|
|
71
|
+
The DataStore contract is narrower than SkillStore. Three methods + `staticCapabilities()`:
|
|
72
72
|
|
|
73
73
|
| Method | What it does | When called |
|
|
74
74
|
|---|---|---|
|
|
75
|
-
| `query(filters)` | Read
|
|
76
|
-
| `write(entry)` | Persist a new
|
|
75
|
+
| `query(filters)` | Read records by mode + filter; return `PortableData[]` | Every `$ data_read mode=... query=...` op |
|
|
76
|
+
| `write(entry)` | Persist a new record; return `{id, created_at}` | Every `$ data_write content=... -> R` op + `data_write` skill notify routes |
|
|
77
77
|
| `manifest()` | Capability snapshot for `runtime_capabilities` discovery | At startup + on-demand from MCP clients |
|
|
78
78
|
|
|
79
|
-
Per the curated-subset framing in `src/connectors/types.ts`, `
|
|
79
|
+
Per the curated-subset framing in `src/connectors/types.ts`, `PortableData` has a 4-tier field model:
|
|
80
80
|
|
|
81
81
|
1. **Core fields** — `id`, `summary`, `detail`, `score` (always-meaningful)
|
|
82
82
|
2. **Curated substrate subset** — top-level fields with portable concepts: `domain_tags`, `payload_type`, `knowledge_type`, `pinned`, `confidence`, `thread_status`, `recipients`, `expires_at`, `agent_id`, `vault`
|
|
@@ -89,8 +89,8 @@ Per the curated-subset framing in `src/connectors/types.ts`, `PortableMemory` ha
|
|
|
89
89
|
|
|
90
90
|
`QueryFilters.mode` is the dispatch axis:
|
|
91
91
|
|
|
92
|
-
- **`"fts"`** — full-text search; substrate-defined (BM25, BM25F, etc.). `
|
|
93
|
-
- **`"semantic"`** — embedding-based similarity. Vector-DB substrates support this; `
|
|
92
|
+
- **`"fts"`** — full-text search; substrate-defined (BM25, BM25F, etc.). `SqliteDataStore` supports this via FTS5.
|
|
93
|
+
- **`"semantic"`** — embedding-based similarity. Vector-DB substrates support this; `SqliteDataStore` does not.
|
|
94
94
|
- **`"rerank"`** — substrate-defined hybrid (e.g., FTS retrieve + embedding rerank). Optional.
|
|
95
95
|
- **substrate-specific strings** — your fork can define its own modes; document them in `manifest().manifest.supported_modes`.
|
|
96
96
|
|
|
@@ -111,9 +111,9 @@ Honor what your substrate supports; ignore the rest. Document the supported set
|
|
|
111
111
|
|
|
112
112
|
## `write()` semantics
|
|
113
113
|
|
|
114
|
-
The `
|
|
114
|
+
The `DataWrite` shape:
|
|
115
115
|
|
|
116
|
-
- **`content`** (required) — the
|
|
116
|
+
- **`content`** (required) — the record body
|
|
117
117
|
- **`tags`** (optional) — routed to your substrate's tag mechanism
|
|
118
118
|
- **`recipients`** (optional) — advisory hint; substrates with alerting (e.g., AMP's mailbox model) use it
|
|
119
119
|
- **`expires_at`** (optional) — unix seconds; substrates with TTL honor it
|
|
@@ -121,22 +121,22 @@ The `MemoryWrite` shape:
|
|
|
121
121
|
|
|
122
122
|
Return `{id, created_at}` — the substrate-assigned identifier + creation timestamp (unix seconds).
|
|
123
123
|
|
|
124
|
-
If your substrate doesn't support writes (read-only
|
|
124
|
+
If your substrate doesn't support writes (read-only store like a search index over a static corpus), set `supports_writes: false` in `staticCapabilities()` and throw from `write()`. The runtime + lint will respect the flag.
|
|
125
125
|
|
|
126
126
|
## Wiring against the dashboard / MCP
|
|
127
127
|
|
|
128
|
-
Runtime hosts honor whichever
|
|
128
|
+
Runtime hosts honor whichever DataStore impl your registry has. To make your fork visible through `skillfile dashboard`:
|
|
129
129
|
|
|
130
|
-
- Write a custom bootstrap that constructs the runtime with your
|
|
130
|
+
- Write a custom bootstrap that constructs the runtime with your DataStore (see `src/bootstrap.ts` for the reference shape)
|
|
131
131
|
- OR (planned future) declare in `~/.skillscript/connectors.json`:
|
|
132
132
|
|
|
133
133
|
```json
|
|
134
134
|
{
|
|
135
135
|
"substrate": {
|
|
136
|
-
"
|
|
136
|
+
"data_store": {
|
|
137
137
|
"type": "custom",
|
|
138
|
-
"module": "./my-
|
|
139
|
-
"export": "
|
|
138
|
+
"module": "./my-data-store.js",
|
|
139
|
+
"export": "MyDataStore",
|
|
140
140
|
"config": { "pineconeApiKey": "${PINECONE_KEY}" }
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -145,11 +145,11 @@ Runtime hosts honor whichever MemoryStore impl your registry has. To make your f
|
|
|
145
145
|
|
|
146
146
|
Current limitation: sync `bootstrap()` can't dynamic-import, so the `custom` form surfaces an error today. Programmatic bootstrap is the path until async-bootstrap support lands.
|
|
147
147
|
|
|
148
|
-
##
|
|
148
|
+
## DataStore vs SkillStore differences
|
|
149
149
|
|
|
150
150
|
Both are substrate-agnostic, but they differ in shape:
|
|
151
151
|
|
|
152
|
-
| Aspect | SkillStore |
|
|
152
|
+
| Aspect | SkillStore | DataStore |
|
|
153
153
|
|---|---|---|
|
|
154
154
|
| Methods | 8 (load, query, metadata, versions, store, delete, update_status, manifest) | 3 (query, write, manifest) |
|
|
155
155
|
| Versioning | First-class (`versions()`, `load(name, version)`) | Substrate-side (e.g., `supersededById` in AMP); not in contract |
|
|
@@ -158,12 +158,12 @@ Both are substrate-agnostic, but they differ in shape:
|
|
|
158
158
|
| Curated-subset fields | None | Many (`domain_tags`, `pinned`, `confidence`, `recipients`, etc.) |
|
|
159
159
|
| Forking complexity | Higher (multi-method state machine) | Lower (three methods, simpler semantics) |
|
|
160
160
|
|
|
161
|
-
The narrower contract is why
|
|
161
|
+
The narrower contract is why DataStoreTemplate ships sooner + smaller than SkillStoreTemplate.
|
|
162
162
|
|
|
163
163
|
## Further reading
|
|
164
164
|
|
|
165
165
|
- **[`../../../docs/configuration.md`](../../../docs/configuration.md)** — substrate selection via `connectors.json`
|
|
166
166
|
- **[`../../../docs/adopter-playbook.md`](../../../docs/adopter-playbook.md)** — programmatic-bootstrap patterns; two-instance posture
|
|
167
|
-
- **`src/connectors/types.ts`** — authoritative `
|
|
167
|
+
- **`src/connectors/types.ts`** — authoritative `DataStore` interface, `PortableData`, `QueryFilters`, `DataWrite` types
|
|
168
168
|
- **`src/testing/conformance.ts`** — the per-contract conformance test suites
|
|
169
|
-
- **`src/connectors/
|
|
169
|
+
- **`src/connectors/data-store.ts`** — `SqliteDataStore` reference impl + schema
|
|
@@ -113,7 +113,7 @@ Adopters wire their custom LocalModel **programmatically** from their bootstrap:
|
|
|
113
113
|
registry.registerLocalModel("default", new MyLocalModel({ /* config */ }));
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
Declarative custom-LocalModel via `connectors.json` `substrate.local_model: {type: "custom", module, export, config}` form is **deferred** until async-bootstrap support lands. Same limitation applies to `SkillStore` and `
|
|
116
|
+
Declarative custom-LocalModel via `connectors.json` `substrate.local_model: {type: "custom", module, export, config}` form is **deferred** until async-bootstrap support lands. Same limitation applies to `SkillStore` and `DataStore` custom forms — sync `bootstrap()` can't `await import()`. Not LocalModel-specific.
|
|
117
117
|
|
|
118
118
|
## Wiring against the dashboard / MCP
|
|
119
119
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* - `LocalModelMcpConnector` — bridges a registered LocalModel as
|
|
16
16
|
* `$ llm prompt=...` MCP dispatch. Auto-wired when LocalModel substrate
|
|
17
17
|
* is configured.
|
|
18
|
-
* - `
|
|
19
|
-
* `$
|
|
18
|
+
* - `DataStoreMcpConnector` — bridges a registered DataStore as
|
|
19
|
+
* `$ data_read mode=...` MCP dispatch. Auto-wired when DataStore substrate
|
|
20
20
|
* is configured.
|
|
21
21
|
*
|
|
22
22
|
* Fork from this template when none of those fit — e.g.:
|
|
@@ -9,7 +9,7 @@ A skeleton `McpConnector` implementation for adopters writing their own. Not run
|
|
|
9
9
|
| `RemoteMcpConnector` | Stdio bridging to remote MCP servers (`npx mcp-remote ...`). YouTrack, GitHub, Linear, most adopter MCP wiring goes through this. JSON-configurable via `connectors.json`. |
|
|
10
10
|
| `CallbackMcpConnector` | Wraps a JS function. Test rigs + embedder-wired transports where the dispatch is local code. |
|
|
11
11
|
| `LocalModelMcpConnector` | Bridges a registered `LocalModel` as `$ llm prompt=...`. Auto-wired when `substrate.local_model` is set. |
|
|
12
|
-
| `
|
|
12
|
+
| `DataStoreMcpConnector` | Bridges a registered `DataStore` as `$ data_read mode=...`. Auto-wired when `substrate.data_store` is set. |
|
|
13
13
|
|
|
14
14
|
**Fork this template only when none of those fit** — e.g.:
|
|
15
15
|
|
|
@@ -72,7 +72,7 @@ The bundled impls are the canonical reference:
|
|
|
72
72
|
- **`src/connectors/mcp-remote.ts`** — `RemoteMcpConnector`. Most comprehensive: stdio framing (LSP and newline), child process lifecycle (spawn → initialize → tools/list cache → SIGTERM/SIGKILL on dispose), `fromConfig` factory with strict validation, per-message timeout discipline. The closest reference for any transport that needs robust lifecycle management.
|
|
73
73
|
- **`src/connectors/mcp.ts`** — `CallbackMcpConnector`. Minimal reference: 60 LOC. The closest reference for embedder-wired transports.
|
|
74
74
|
|
|
75
|
-
When in doubt, read both + the bridge classes (`local-model-mcp.ts`, `
|
|
75
|
+
When in doubt, read both + the bridge classes (`local-model-mcp.ts`, `data-store-mcp.ts`) for how single-substrate bridges work.
|
|
76
76
|
|
|
77
77
|
## Contract surface (2 methods)
|
|
78
78
|
|
|
@@ -131,15 +131,15 @@ Two registration paths:
|
|
|
131
131
|
|
|
132
132
|
The declarative path is the canonical adopter pattern for stdio-bridged remote MCPs. Your fork is for transports the declarative path can't express.
|
|
133
133
|
|
|
134
|
-
## McpConnector vs. SkillStore /
|
|
134
|
+
## McpConnector vs. SkillStore / DataStore differences
|
|
135
135
|
|
|
136
|
-
| Aspect | McpConnector | SkillStore /
|
|
136
|
+
| Aspect | McpConnector | SkillStore / DataStore |
|
|
137
137
|
|---|---|---|
|
|
138
138
|
| Methods | 2 (call, manifest) | 3-8 (more state machine) |
|
|
139
139
|
| Cardinality | Many instances per deployment | One singleton per slot |
|
|
140
140
|
| Substrate config | Per-instance via top-level keys | `substrate` section short/object/custom |
|
|
141
141
|
| Class extensibility | `registerConnectorClass()` for adopter-custom classes | Programmatic bootstrap (or `substrate.skill_store: {type: "custom", ...}` once async-bootstrap lands) |
|
|
142
|
-
| Auto-wired bridges | `llm` + `
|
|
142
|
+
| Auto-wired bridges | `llm` + `data_read` + `data_write` (LocalModel + DataStore exposed via bridge connectors) | n/a (these ARE the substrates being bridged) |
|
|
143
143
|
|
|
144
144
|
McpConnector is fundamentally the "dispatch to external tools" surface — narrowest contract, broadest range of impls.
|
|
145
145
|
|
|
@@ -7,9 +7,9 @@ Worked examples + fork-me templates for adopter-written connectors. The bundled
|
|
|
7
7
|
| Contract | Bundled defaults | Worked example | Fork template |
|
|
8
8
|
|---|---|---|---|
|
|
9
9
|
| `SkillStore` | `FilesystemSkillStore`, `SqliteSkillStore` (in `src/connectors/`) | — | **[SkillStoreTemplate/](./SkillStoreTemplate/)** |
|
|
10
|
-
| `
|
|
10
|
+
| `DataStore` | `SqliteDataStore` (in `src/connectors/`) | — | **[DataStoreTemplate/](./DataStoreTemplate/)** |
|
|
11
11
|
| `LocalModel` | `OllamaLocalModel` (in `src/connectors/`; opt-in via substrate config) | — | **[LocalModelTemplate/](./LocalModelTemplate/)** |
|
|
12
|
-
| `McpConnector` | `RemoteMcpConnector`, `CallbackMcpConnector`, `LocalModelMcpConnector`, `
|
|
12
|
+
| `McpConnector` | `RemoteMcpConnector`, `CallbackMcpConnector`, `LocalModelMcpConnector`, `DataStoreMcpConnector` (in `src/connectors/`) | — | **[McpConnectorTemplate/](./McpConnectorTemplate/)** |
|
|
13
13
|
| `AgentConnector` | `NoOpAgentConnector` (in `src/connectors/`) | **[HttpWebhookAgentConnector/](./HttpWebhookAgentConnector/)** | — |
|
|
14
14
|
|
|
15
15
|
**Bundled defaults** are runnable out of the box — wired through `connectors.json` substrate config or programmatic bootstrap.
|
|
@@ -35,7 +35,7 @@ Worked examples + fork-me templates for adopter-written connectors. The bundled
|
|
|
35
35
|
- **[`docs/configuration.md`](../../docs/configuration.md)** — substrate selection via `connectors.json`; programmatic-bootstrap vs declarative patterns
|
|
36
36
|
- **[`docs/adopter-playbook.md`](../../docs/adopter-playbook.md)** — Case 1 vs Case 2 wiring decisions; two-instance posture; upstream-merge-friendly conventions
|
|
37
37
|
- **[`docs/connector-contract-reference.md`](../../docs/connector-contract-reference.md)** — interface contracts for adopter agents writing connector impls
|
|
38
|
-
- **`src/connectors/types.ts`** — authoritative contract interfaces (SkillStore,
|
|
38
|
+
- **`src/connectors/types.ts`** — authoritative contract interfaces (SkillStore, DataStore, LocalModel, McpConnector)
|
|
39
39
|
- **`src/testing/conformance.ts`** — the per-contract conformance test suites
|
|
40
40
|
|
|
41
41
|
## Naming convention
|
|
@@ -5,7 +5,7 @@ A skeleton `SkillStore` implementation for adopters writing their own. Not runna
|
|
|
5
5
|
Use this when you want skillscript skills backed by:
|
|
6
6
|
- A database the bundled impls don't cover (Postgres, MySQL, MongoDB, etc.)
|
|
7
7
|
- A vector store (Pinecone, Weaviate, Qdrant)
|
|
8
|
-
- A memory
|
|
8
|
+
- A data store backing (memory broker like AMP, hosted memory API, vector DB, etc.)
|
|
9
9
|
- An HTTP CRUD service
|
|
10
10
|
- Anything else with a query/write interface
|
|
11
11
|
|
|
@@ -138,7 +138,7 @@ export class SkillStoreTemplate implements SkillStore {
|
|
|
138
138
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
139
139
|
async versions(_name: string): Promise<VersionInfo[]> {
|
|
140
140
|
// TODO — return version chain. SQL substrates: SELECT FROM skill_versions
|
|
141
|
-
// ORDER BY changed_at.
|
|
141
|
+
// ORDER BY changed_at. DataStore-style: query memories with the right tag.
|
|
142
142
|
throw new Error("TODO: versions() — return VersionInfo[] chronologically.");
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
// **When to write your own bootstrap.** The bundled `bootstrap()` from
|
|
5
5
|
// `skillscript-runtime` wires `FilesystemSkillStore` + `OllamaLocalModel` +
|
|
6
|
-
// `
|
|
7
|
-
// different substrates (your own
|
|
6
|
+
// `SqliteDataStore` + the v0.7.2 bridges. If your deployment uses
|
|
7
|
+
// different substrates (your own data store, a hosted LLM API, an MCP
|
|
8
8
|
// server for agent delivery, etc.), write your own bootstrap rather than
|
|
9
9
|
// modifying the bundled one. Prevents merge conflicts on every upstream
|
|
10
10
|
// release that touches `src/bootstrap.ts`.
|
|
@@ -83,17 +83,24 @@ const registry = new Registry();
|
|
|
83
83
|
const skillStore = new FilesystemSkillStore(config.skillsDir ?? `${HOME}/skills`);
|
|
84
84
|
registry.registerSkillStore("primary", skillStore);
|
|
85
85
|
|
|
86
|
-
// Wire your own LocalModel,
|
|
86
|
+
// Wire your own LocalModel, DataStore, AgentConnector here. For typed-
|
|
87
87
|
// contract impls, this is the "case 1" wiring — substrate-portable.
|
|
88
88
|
// registry.registerLocalModel("default", new MyHostedLlmAdapter(...));
|
|
89
|
-
// registry.
|
|
89
|
+
// registry.registerDataStore("primary", new MyDataStoreAdapter(...));
|
|
90
90
|
// registry.registerAgentConnector("primary", new MyAgentHarnessAdapter(...));
|
|
91
91
|
|
|
92
|
-
// Then wire bridges if you want the canonical `$ llm` / `$
|
|
93
|
-
//
|
|
92
|
+
// Then wire bridges if you want the canonical `$ llm` / `$ data_read` /
|
|
93
|
+
// `$ data_write` surfaces. The DataStore bridge handles BOTH read and
|
|
94
|
+
// write via toolName discrimination; register the same instance under both
|
|
95
|
+
// connector names so bare-form name-match resolution picks the right route.
|
|
96
|
+
// import { LocalModelMcpConnector, DataStoreMcpConnector } from "skillscript-runtime";
|
|
94
97
|
// registry.registerMcpConnector("llm", new LocalModelMcpConnector(registry.getLocalModel("default")));
|
|
95
|
-
// const ms = registry.
|
|
96
|
-
// if (ms !== undefined)
|
|
98
|
+
// const ms = registry.listDataStores().find((e) => e.name === "primary");
|
|
99
|
+
// if (ms !== undefined) {
|
|
100
|
+
// const dataBridge = new DataStoreMcpConnector(ms.instance);
|
|
101
|
+
// registry.registerMcpConnector("data_read", dataBridge);
|
|
102
|
+
// registry.registerMcpConnector("data_write", dataBridge);
|
|
103
|
+
// }
|
|
97
104
|
|
|
98
105
|
// Wire any connectors.json instances.
|
|
99
106
|
for (const c of connectors) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Onboarding scaffold — file-backed
|
|
1
|
+
# Onboarding scaffold — file-backed data store + OpenAI + tmux-shell
|
|
2
2
|
|
|
3
3
|
A complete adopter-deployment example demonstrating substrate-portable wiring for skillscript-runtime. **~200 LOC across three adapter files** plus bootstrap. Copy this directory and modify for your own deployment.
|
|
4
4
|
|
|
@@ -6,11 +6,11 @@ A complete adopter-deployment example demonstrating substrate-portable wiring fo
|
|
|
6
6
|
|
|
7
7
|
Skillscript's substrate-portability story is conditional: **typed-contract wiring (Case 1)** keeps skills portable across substrates; **MCP-tools wiring (Case 2)** locks skills to a specific substrate. This scaffold is Case 1 end-to-end.
|
|
8
8
|
|
|
9
|
-
- **
|
|
9
|
+
- **DataStore** → `FileDataStore` (JSON file with simple substring FTS)
|
|
10
10
|
- **LocalModel** → `OpenAILocalModel` (HTTP to Chat Completions API)
|
|
11
11
|
- **AgentConnector** → `TmuxShellAgentConnector` (delivers to tmux sessions via `send-keys`)
|
|
12
12
|
|
|
13
|
-
Each impl conforms to the typed contract from `skillscript-runtime/connectors`. Skills authored against this scaffold use the canonical `$ llm prompt=...` and `$
|
|
13
|
+
Each impl conforms to the typed contract from `skillscript-runtime/connectors`. Skills authored against this scaffold use the canonical `$ llm prompt=...` and `$ data_read mode=fts query=...` surfaces — *the same calls would work against any other Case-1-wired substrate* (Pinecone, Ollama, Anthropic API, etc.).
|
|
14
14
|
|
|
15
15
|
## Quick start
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@ cd ~/my-skillscript-deployment
|
|
|
25
25
|
# 3. Set up env
|
|
26
26
|
export SKILLSCRIPT_HOME=$(pwd)
|
|
27
27
|
export OPENAI_API_KEY=sk-...
|
|
28
|
-
cp
|
|
28
|
+
cp data.example.json data.json # initial data store seed
|
|
29
29
|
|
|
30
30
|
# 4. Init skillscript dir layout
|
|
31
31
|
skillfile init --here
|
|
@@ -44,16 +44,16 @@ node --loader ts-node/esm bootstrap.ts
|
|
|
44
44
|
|
|
45
45
|
| File | Purpose | LOC |
|
|
46
46
|
|---|---|---|
|
|
47
|
-
| `file-
|
|
47
|
+
| `file-data-store.ts` | `DataStore` impl — JSON file substrate | ~95 |
|
|
48
48
|
| `openai-local-model.ts` | `LocalModel` impl — Chat Completions API | ~85 |
|
|
49
49
|
| `tmux-shell-agent-connector.ts` | `AgentConnector` impl — tmux send-keys | ~75 |
|
|
50
50
|
| `bootstrap.ts` | Wiring — Registry, bridges, scheduler, MCP server | ~75 |
|
|
51
51
|
| `connectors.json` | Example adopter-MCP wiring (empty by default) | — |
|
|
52
|
-
| `
|
|
52
|
+
| `data.example.json` | Seed data file with three example records | — |
|
|
53
53
|
|
|
54
54
|
## Two-instance posture
|
|
55
55
|
|
|
56
|
-
To run this scaffold *alongside* an existing skillscript dev instance, just copy the scaffold to a separate directory with its own `skillscript.config.json` pointing at different `dashboard.port` + `skillsDir` + `
|
|
56
|
+
To run this scaffold *alongside* an existing skillscript dev instance, just copy the scaffold to a separate directory with its own `skillscript.config.json` pointing at different `dashboard.port` + `skillsDir` + `dataDbPath` etc. The `--config <path>` CLI flag selects which config to use; no two instances will collide on disk or port.
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
# dev instance on default port
|
|
@@ -67,14 +67,13 @@ skillfile dashboard --config ./adopter.config.json # contains "dashboard": { "p
|
|
|
67
67
|
|
|
68
68
|
For your real deployment, swap each adapter:
|
|
69
69
|
|
|
70
|
-
- `
|
|
70
|
+
- `FileDataStore` → your actual data store (Pinecone, Postgres-pgvector, Obsidian-backed, in-house store, memory broker, etc.)
|
|
71
71
|
- `OpenAILocalModel` → your actual LLM (Ollama via the bundled `OllamaLocalModel`, Anthropic via your own adapter, hosted-OpenAI as shown, etc.)
|
|
72
72
|
- `TmuxShellAgentConnector` → your actual agent delivery (webhook POST, named-pipe write, your harness's API, etc.)
|
|
73
73
|
|
|
74
|
-
The skill bodies don't need to change. `$ llm prompt=...` keeps working; `$
|
|
74
|
+
The skill bodies don't need to change. `$ llm prompt=...` keeps working; `$ data_read mode=fts query=...` keeps working. That's the substrate-portability claim, validated by you wiring different substrates against the same typed contracts.
|
|
75
75
|
|
|
76
76
|
## Where to go next
|
|
77
77
|
|
|
78
78
|
- **Adopter playbook** — `docs/adopter-playbook.md` walks through Case 1 vs Case 2 wiring patterns
|
|
79
79
|
- **Custom bootstrap walkthrough** — `examples/custom-bootstrap.example.ts` shows registering custom McpConnector classes via `registerConnectorClass`
|
|
80
|
-
- **v0.8.x roadmap** — `$ memory_write` ships in v0.8.x bundled with the auth model. When that lands, extend `FileMemoryStore` with the corresponding `write()` method.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// Onboarding scaffold: complete bootstrap wiring file-backed
|
|
1
|
+
// Onboarding scaffold: complete bootstrap wiring file-backed data store +
|
|
2
2
|
// OpenAI LLM + tmux-shell AgentConnector. v0.7.3.
|
|
3
3
|
//
|
|
4
4
|
// Copy this file into your deployment and modify substrate choices to
|
|
5
5
|
// match your environment. The shape is:
|
|
6
6
|
// 1. Construct a Registry
|
|
7
|
-
// 2. Register your substrate impls (SkillStore + LocalModel +
|
|
8
|
-
// 3. Wire bridges so canonical `$ llm` / `$
|
|
7
|
+
// 2. Register your substrate impls (SkillStore + LocalModel + DataStore + AgentConnector)
|
|
8
|
+
// 3. Wire bridges so canonical `$ llm` / `$ data_read` dispatch works
|
|
9
9
|
// 4. Load connectors.json + skillscript.config.json
|
|
10
10
|
// 5. Wire scheduler + MCP server
|
|
11
11
|
// 6. Start
|
|
@@ -20,15 +20,15 @@ import {
|
|
|
20
20
|
import { Scheduler } from "skillscript-runtime/scheduler";
|
|
21
21
|
import { FilesystemTraceStore } from "skillscript-runtime/trace";
|
|
22
22
|
import { McpServer } from "skillscript-runtime/mcp-server";
|
|
23
|
-
// Note: LocalModelMcpConnector +
|
|
23
|
+
// Note: LocalModelMcpConnector + DataStoreMcpConnector are bridge classes
|
|
24
24
|
// already exported from skillscript-runtime/connectors. They wrap any
|
|
25
25
|
// typed-contract impl as an MCP-dispatchable connector.
|
|
26
26
|
import {
|
|
27
27
|
LocalModelMcpConnector,
|
|
28
|
-
|
|
28
|
+
DataStoreMcpConnector,
|
|
29
29
|
} from "skillscript-runtime/connectors";
|
|
30
30
|
|
|
31
|
-
import {
|
|
31
|
+
import { FileDataStore } from "./file-data-store.js";
|
|
32
32
|
import { OpenAILocalModel } from "./openai-local-model.js";
|
|
33
33
|
import { TmuxShellAgentConnector } from "./tmux-shell-agent-connector.js";
|
|
34
34
|
|
|
@@ -43,11 +43,11 @@ const registry = new Registry();
|
|
|
43
43
|
const skillStore = new FilesystemSkillStore(config.skillsDir ?? `${HOME}/skills`);
|
|
44
44
|
registry.registerSkillStore("primary", skillStore);
|
|
45
45
|
|
|
46
|
-
// File-backed
|
|
47
|
-
const
|
|
48
|
-
filePath: config.
|
|
46
|
+
// File-backed data store at $SKILLSCRIPT_HOME/data.json
|
|
47
|
+
const dataStore = new FileDataStore({
|
|
48
|
+
filePath: config.dataDbPath ?? `${HOME}/data.json`,
|
|
49
49
|
});
|
|
50
|
-
registry.
|
|
50
|
+
registry.registerDataStore("primary", dataStore);
|
|
51
51
|
|
|
52
52
|
// OpenAI LLM — reads OPENAI_API_KEY from env.
|
|
53
53
|
const openai = new OpenAILocalModel({ defaultModel: "gpt-4o-mini" });
|
|
@@ -63,10 +63,15 @@ const agentConnector = new TmuxShellAgentConnector({
|
|
|
63
63
|
});
|
|
64
64
|
registry.registerAgentConnector("primary", agentConnector);
|
|
65
65
|
|
|
66
|
-
// Step 3: wire bridges so `$ llm` / `$
|
|
67
|
-
// adopter substrates above (case 1 typed-contract wiring —
|
|
66
|
+
// Step 3: wire bridges so `$ llm` / `$ data_read` / `$ data_write` dispatch
|
|
67
|
+
// through the adopter substrates above (case 1 typed-contract wiring —
|
|
68
|
+
// portable). The DataStore bridge handles BOTH read and write via toolName
|
|
69
|
+
// discrimination; register the same instance under both connector names so
|
|
70
|
+
// bare-form name-match resolution picks the right route.
|
|
68
71
|
registry.registerMcpConnector("llm", new LocalModelMcpConnector(openai));
|
|
69
|
-
|
|
72
|
+
const dataBridge = new DataStoreMcpConnector(dataStore);
|
|
73
|
+
registry.registerMcpConnector("data_read", dataBridge);
|
|
74
|
+
registry.registerMcpConnector("data_write", dataBridge);
|
|
70
75
|
|
|
71
76
|
// Step 4: wire connectors.json instances (adopter-defined MCP servers).
|
|
72
77
|
for (const c of connectors) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Onboarding scaffold connectors.json — wires example MCP servers an adopter might add alongside the bundled bridges. Most onboarding deployments don't need anything here; the file-backed
|
|
2
|
+
"_comment": "Onboarding scaffold connectors.json — wires example MCP servers an adopter might add alongside the bundled bridges. Most onboarding deployments don't need anything here; the file-backed data store + OpenAI LLM + tmux-shell wiring lives in bootstrap.ts. This file is the place for adopter-specific MCP servers (your in-house tools, a YouTrack instance, a custom agent API, etc.).",
|
|
3
3
|
|
|
4
4
|
"_example_remote_mcp": {
|
|
5
5
|
"class": "RemoteMcpConnector",
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
// Onboarding scaffold: file-backed
|
|
1
|
+
// Onboarding scaffold: file-backed DataStore. v0.7.3.
|
|
2
2
|
//
|
|
3
3
|
// JSON file as the substrate; simple JS substring + token match for "fts"
|
|
4
4
|
// queries; reranks by recency. Adopters copy this file and modify for
|
|
5
5
|
// their concrete substrate (e.g., swap the JSON file for a Postgres
|
|
6
6
|
// table, the substring match for actual full-text search, etc.).
|
|
7
7
|
//
|
|
8
|
-
// **Scope.** Read-only `query()` per the v0.7.2
|
|
8
|
+
// **Scope.** Read-only `query()` per the v0.7.2 DataStore contract.
|
|
9
9
|
// `write()` is deferred to v0.8.x bundled with the auth model — when
|
|
10
10
|
// that lands, extend this file with the matching `write()` method.
|
|
11
11
|
|
|
12
12
|
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
14
|
import type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
DataStore,
|
|
16
|
+
DataWrite,
|
|
17
|
+
DataWriteRecord,
|
|
18
|
+
PortableData,
|
|
19
19
|
QueryFilters,
|
|
20
20
|
ManifestInfo,
|
|
21
21
|
StaticCapabilities,
|
|
22
22
|
} from "skillscript-runtime/connectors";
|
|
23
23
|
|
|
24
|
-
export interface
|
|
25
|
-
/** Absolute path to the JSON file holding the
|
|
24
|
+
export interface FileDataStoreConfig {
|
|
25
|
+
/** Absolute path to the JSON file holding the data records. */
|
|
26
26
|
filePath: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
interface
|
|
30
|
-
/** Optional substrate-specific fields go in `metadata`; everything top-level matches
|
|
29
|
+
interface FileDataRecord extends PortableData {
|
|
30
|
+
/** Optional substrate-specific fields go in `metadata`; everything top-level matches PortableData. */
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export class
|
|
33
|
+
export class FileDataStore implements DataStore {
|
|
34
34
|
static staticCapabilities(): StaticCapabilities {
|
|
35
35
|
return {
|
|
36
|
-
connector_type: "
|
|
37
|
-
implementation: "
|
|
36
|
+
connector_type: "data_store",
|
|
37
|
+
implementation: "FileDataStore",
|
|
38
38
|
contract_version: "1.0.0",
|
|
39
39
|
features: {
|
|
40
40
|
supports_fts: true,
|
|
@@ -44,9 +44,9 @@ export class FileMemoryStore implements MemoryStore {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
constructor(private readonly config:
|
|
47
|
+
constructor(private readonly config: FileDataStoreConfig) {}
|
|
48
48
|
|
|
49
|
-
async query(filters: QueryFilters): Promise<
|
|
49
|
+
async query(filters: QueryFilters): Promise<PortableData[]> {
|
|
50
50
|
const records = this.loadFile();
|
|
51
51
|
const queryTerms = filters.query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
52
52
|
|
|
@@ -71,13 +71,13 @@ export class FileMemoryStore implements MemoryStore {
|
|
|
71
71
|
return scored.slice(0, filters.limit).map((s) => s.record);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
async write(entry:
|
|
74
|
+
async write(entry: DataWrite): Promise<DataWriteRecord> {
|
|
75
75
|
const records = this.loadFile();
|
|
76
76
|
const id = randomUUID();
|
|
77
77
|
const created_at = Math.floor(Date.now() / 1000);
|
|
78
78
|
const firstLine = entry.content.split("\n")[0] ?? entry.content;
|
|
79
79
|
const summary = firstLine.length > 200 ? firstLine.slice(0, 197) + "..." : firstLine;
|
|
80
|
-
const newRecord:
|
|
80
|
+
const newRecord: FileDataRecord = {
|
|
81
81
|
id,
|
|
82
82
|
summary,
|
|
83
83
|
detail: entry.content,
|
|
@@ -92,7 +92,7 @@ export class FileMemoryStore implements MemoryStore {
|
|
|
92
92
|
},
|
|
93
93
|
}
|
|
94
94
|
: {}),
|
|
95
|
-
} as
|
|
95
|
+
} as FileDataRecord;
|
|
96
96
|
records.push(newRecord);
|
|
97
97
|
writeFileSync(this.config.filePath, JSON.stringify(records, null, 2), "utf8");
|
|
98
98
|
return { id, created_at };
|
|
@@ -102,25 +102,36 @@ export class FileMemoryStore implements MemoryStore {
|
|
|
102
102
|
return {
|
|
103
103
|
capabilities_version: "1",
|
|
104
104
|
manifest: {
|
|
105
|
-
kind: "file-
|
|
105
|
+
kind: "file-data-store",
|
|
106
106
|
file_path: this.config.filePath,
|
|
107
107
|
record_count: this.loadFile().length,
|
|
108
108
|
supports_write: true,
|
|
109
|
+
// v0.14.1 strict-filters: the bridge enforces every non-base filter
|
|
110
|
+
// key in `query()` calls against this declared set, throwing
|
|
111
|
+
// UnsupportedFilterError for unknowns. This reference impl's
|
|
112
|
+
// substring scorer doesn't actually filter on any field (it
|
|
113
|
+
// ignores everything beyond `query` + `limit`), so the honest
|
|
114
|
+
// declaration is `[]`. Adopters who extend `query()` to honor
|
|
115
|
+
// `domain_tags` / `payload_type` / etc. should add those names
|
|
116
|
+
// here so the bridge stops rejecting them. Callers that need
|
|
117
|
+
// to pass advisory filters this impl ignores can opt out per-
|
|
118
|
+
// call via `permissive_filters: true`.
|
|
119
|
+
supported_filters: [],
|
|
109
120
|
},
|
|
110
121
|
};
|
|
111
122
|
}
|
|
112
123
|
|
|
113
|
-
private loadFile():
|
|
124
|
+
private loadFile(): FileDataRecord[] {
|
|
114
125
|
if (!existsSync(this.config.filePath)) return [];
|
|
115
126
|
try {
|
|
116
127
|
const raw = readFileSync(this.config.filePath, "utf8");
|
|
117
128
|
const parsed: unknown = JSON.parse(raw);
|
|
118
129
|
if (!Array.isArray(parsed)) {
|
|
119
|
-
throw new Error(`
|
|
130
|
+
throw new Error(`FileDataStore: '${this.config.filePath}' top-level must be an array of data records.`);
|
|
120
131
|
}
|
|
121
|
-
return parsed as
|
|
132
|
+
return parsed as FileDataRecord[];
|
|
122
133
|
} catch (err) {
|
|
123
|
-
throw new Error(`
|
|
134
|
+
throw new Error(`FileDataStore: failed to read '${this.config.filePath}': ${(err as Error).message}`);
|
|
124
135
|
}
|
|
125
136
|
}
|
|
126
137
|
}
|