skillscript-runtime 0.10.0 → 0.13.2
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 +18 -42
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +18 -8
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +5 -6
- package/dist/cli.js.map +1 -1
- package/dist/connectors/agent-noop.d.ts +2 -2
- package/dist/connectors/agent-noop.d.ts.map +1 -1
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js +5 -3
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/local-model-mcp.d.ts +3 -3
- package/dist/connectors/local-model-mcp.d.ts.map +1 -1
- package/dist/connectors/local-model.d.ts +30 -11
- package/dist/connectors/local-model.d.ts.map +1 -1
- package/dist/connectors/local-model.js +44 -12
- package/dist/connectors/local-model.js.map +1 -1
- package/dist/connectors/mcp-remote.d.ts +3 -3
- package/dist/connectors/mcp-remote.d.ts.map +1 -1
- package/dist/connectors/mcp.d.ts +3 -3
- package/dist/connectors/mcp.d.ts.map +1 -1
- package/dist/connectors/memory-store-mcp.d.ts +3 -3
- package/dist/connectors/memory-store-mcp.d.ts.map +1 -1
- package/dist/connectors/memory-store-mcp.js +3 -1
- package/dist/connectors/memory-store-mcp.js.map +1 -1
- package/dist/connectors/memory-store.d.ts +26 -6
- package/dist/connectors/memory-store.d.ts.map +1 -1
- package/dist/connectors/memory-store.js +84 -15
- package/dist/connectors/memory-store.js.map +1 -1
- package/dist/connectors/registry.d.ts +28 -5
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +34 -11
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/skill-store.d.ts +3 -3
- package/dist/connectors/skill-store.d.ts.map +1 -1
- package/dist/connectors/sqlite-skill-store.d.ts +27 -3
- package/dist/connectors/sqlite-skill-store.d.ts.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +85 -58
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/connectors/types.d.ts +86 -9
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js.map +1 -1
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -0
- package/dist/errors.js.map +1 -1
- package/dist/runtime-config.d.ts +0 -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 +24 -29
- package/dist/runtime.js.map +1 -1
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +12 -6
- package/dist/testing/conformance.js.map +1 -1
- package/examples/README.md +13 -13
- package/examples/connectors/LocalModelTemplate/LocalModelTemplate.ts +138 -0
- package/examples/connectors/LocalModelTemplate/README.md +134 -0
- package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +200 -0
- package/examples/connectors/McpConnectorTemplate/README.md +153 -0
- package/examples/connectors/MemoryStoreTemplate/MemoryStoreTemplate.ts +155 -0
- package/examples/connectors/MemoryStoreTemplate/README.md +169 -0
- package/examples/connectors/README.md +47 -0
- package/examples/connectors/SkillStoreTemplate/README.md +143 -0
- package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +199 -0
- package/examples/{hello.skill.provenance.json → skillscripts/hello.skill.provenance.json} +1 -1
- package/package.json +1 -1
- package/scaffold/config.toml +8 -12
- package/scaffold/connectors.json +4 -4
- /package/examples/{classify-support-ticket.skill.md → skillscripts/classify-support-ticket.skill.md} +0 -0
- /package/examples/{cut-release-tag.skill.md → skillscripts/cut-release-tag.skill.md} +0 -0
- /package/examples/{doc-qa-with-citations.skill.md → skillscripts/doc-qa-with-citations.skill.md} +0 -0
- /package/examples/{feedback-sentiment-scan.skill.md → skillscripts/feedback-sentiment-scan.skill.md} +0 -0
- /package/examples/{hello.skill.md → skillscripts/hello.skill.md} +0 -0
- /package/examples/{morning-brief.skill.md → skillscripts/morning-brief.skill.md} +0 -0
- /package/examples/{queue-length-monitor.skill.md → skillscripts/queue-length-monitor.skill.md} +0 -0
- /package/examples/{service-health-watch.skill.md → skillscripts/service-health-watch.skill.md} +0 -0
- /package/examples/{youtrack-morning-sweep.skill.md → skillscripts/youtrack-morning-sweep.skill.md} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# LocalModelTemplate — fork-me skeleton
|
|
2
|
+
|
|
3
|
+
A skeleton `LocalModel` 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
|
+
|
|
5
|
+
**Use this when you want LocalModel inference backed by:**
|
|
6
|
+
- An OpenAI-compat HTTP endpoint (vLLM, TGI, SGLang, llama.cpp server)
|
|
7
|
+
- A hosted LLM API (OpenAI, Anthropic, Cohere — though most adopters wire those via `RemoteMcpConnector` + the provider's MCP server instead)
|
|
8
|
+
- A custom transport (gRPC, WebSocket, in-process inference)
|
|
9
|
+
- A multi-model gateway
|
|
10
|
+
|
|
11
|
+
The bundled `OllamaLocalModel` (in `src/connectors/local-model.ts`) is Ollama-specific. Fork from here when that doesn't fit.
|
|
12
|
+
|
|
13
|
+
## The two legs
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
LocalModel (choose which connector)
|
|
17
|
+
├── OllamaLocalModel (bundled, Ollama HTTP — src/connectors/local-model.ts)
|
|
18
|
+
└── Your fork from this template
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
LocalModel is the narrowest contract — just 2 methods + staticCapabilities. Most of the work in a fork is substrate-specific (auth, wire format, response parsing), not contract conformance.
|
|
22
|
+
|
|
23
|
+
## Forking workflow
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cp -r examples/connectors/LocalModelTemplate examples/connectors/MyLocalModel
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
1. **Rename the class.** Convention: `<Substrate>LocalModel` (e.g., `OpenAICompatLocalModel`, `AnthropicLocalModel`, `VllmLocalModel`).
|
|
30
|
+
2. **Define your config interface.** Edit `LocalModelTemplateConfig` to declare what your substrate needs (endpoint URL, API key, default model, timeout, etc.).
|
|
31
|
+
3. **Implement `run()`** — translate `(prompt, opts)` to your substrate's wire format; dispatch; parse the response text.
|
|
32
|
+
4. **Implement `manifest()`** — return substrate metadata (kind, endpoint, default model, available models when introspectable).
|
|
33
|
+
5. **Update `staticCapabilities()`** to declare what your impl supports.
|
|
34
|
+
6. **Wire from your adopter bootstrap:**
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { Registry } from "skillscript-runtime";
|
|
38
|
+
import { MyLocalModel } from "./MyLocalModel.js";
|
|
39
|
+
|
|
40
|
+
const registry = new Registry();
|
|
41
|
+
registry.registerLocalModel("default", new MyLocalModel({ /* config */ }));
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The auto-wired `$ llm` MCP bridge wraps your impl transparently — once registered, `$ llm prompt="..."` dispatches through your fork without additional wiring.
|
|
45
|
+
|
|
46
|
+
7. **Validate via the conformance suite:**
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { describe, it } from "vitest";
|
|
50
|
+
import { LocalModelConformance } from "skillscript-runtime/testing";
|
|
51
|
+
import { MyLocalModel } from "./MyLocalModel.js";
|
|
52
|
+
|
|
53
|
+
describe("MyLocalModel conformance", () => {
|
|
54
|
+
const tests = LocalModelConformance.buildTests({
|
|
55
|
+
build: () => new MyLocalModel({ /* test config */ }),
|
|
56
|
+
ctor: MyLocalModel,
|
|
57
|
+
});
|
|
58
|
+
for (const t of tests) it(`[${t.category}] ${t.name}`, t.run);
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Reference implementation
|
|
63
|
+
|
|
64
|
+
The bundled `OllamaLocalModel` at `src/connectors/local-model.ts` is the canonical reference (164 LOC):
|
|
65
|
+
|
|
66
|
+
- Ollama HTTP API (`POST /api/generate`, `GET /api/tags`)
|
|
67
|
+
- AbortController timeout pattern
|
|
68
|
+
- Manifest caches successful introspection but NOT failures (retries on next call)
|
|
69
|
+
- Deduped stderr warning for fetch errors
|
|
70
|
+
- Surfaces `fetch_error` in manifest when `/api/tags` introspection fails
|
|
71
|
+
|
|
72
|
+
The error-handling patterns (don't silently cache failures; surface in manifest; dedupe-log) apply to any LocalModel substrate that introspects.
|
|
73
|
+
|
|
74
|
+
## Contract surface (2 methods)
|
|
75
|
+
|
|
76
|
+
LocalModel is the narrowest contract surface in skillscript:
|
|
77
|
+
|
|
78
|
+
| Method | What it does | When called |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `run(prompt, opts)` | Dispatch a prompt; return response text | Every `$ llm prompt="..." -> R` op (via the LocalModelMcpConnector bridge) |
|
|
81
|
+
| `manifest()` | Substrate metadata for discovery | At startup + on-demand from MCP clients |
|
|
82
|
+
|
|
83
|
+
Plus `staticCapabilities()` (required static).
|
|
84
|
+
|
|
85
|
+
### `run()` semantics
|
|
86
|
+
|
|
87
|
+
- **`prompt`** — already-substituted prompt body. Template substitution (`${VAR}` resolution) happens before your impl is called; you get the final string.
|
|
88
|
+
- **`opts.maxTokens`** — optional output length cap. Honor if `supports_max_tokens: true`.
|
|
89
|
+
- **`opts.model`** — optional per-call model override. Useful for adopters with multi-model gateways. Honor if your substrate supports it.
|
|
90
|
+
- **Return** — the response text directly. NOT wrapped in an envelope. The bridge passes this through to skill source as `$(R)`.
|
|
91
|
+
|
|
92
|
+
On dispatch failure: **throw**. The runtime's op-level `(fallback: ...)` machinery catches throws cleanly. Don't return error envelopes silently — that's the silent-recovery footgun pattern.
|
|
93
|
+
|
|
94
|
+
### `manifest()` semantics
|
|
95
|
+
|
|
96
|
+
Surface enough substrate metadata for adopters running `runtime_capabilities` to understand what's wired. Curated fields per `LocalModelManifest`:
|
|
97
|
+
|
|
98
|
+
- `kind` — substrate flavor tag (`"openai-compat"`, `"vllm"`, `"anthropic"`, etc.)
|
|
99
|
+
- `default_model` — configured default model name
|
|
100
|
+
- `endpoint` — URL your impl connects to (helps debugging)
|
|
101
|
+
- `models_available` — list when introspectable
|
|
102
|
+
- `fetch_error` — set when introspection failed; don't silently cache empty array
|
|
103
|
+
|
|
104
|
+
Plus `[key: string]: unknown` for substrate extensions.
|
|
105
|
+
|
|
106
|
+
## A note on declarative wiring
|
|
107
|
+
|
|
108
|
+
Unlike `McpConnector` (which has `registerConnectorClass()` for adopter-extensible declarative wiring via `connectors.json`), `LocalModel` is intrinsically *singleton* per deployment — one default per runtime. There's no equivalent class registry.
|
|
109
|
+
|
|
110
|
+
Adopters wire their custom LocalModel **programmatically** from their bootstrap:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
registry.registerLocalModel("default", new MyLocalModel({ /* config */ }));
|
|
114
|
+
```
|
|
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 `MemoryStore` custom forms — sync `bootstrap()` can't `await import()`. Not LocalModel-specific.
|
|
117
|
+
|
|
118
|
+
## Wiring against the dashboard / MCP
|
|
119
|
+
|
|
120
|
+
Runtime hosts (MCP server + web dashboard) honor whichever LocalModel impl you register. To make your fork visible through `skillfile dashboard`:
|
|
121
|
+
|
|
122
|
+
- Write a custom bootstrap that constructs the runtime with your LocalModel
|
|
123
|
+
- Or register via `registry.registerLocalModel("default", new MyLocalModel(...))` before `bootstrap()` returns
|
|
124
|
+
|
|
125
|
+
The auto-wired `$ llm` MCP bridge picks up your registration automatically — skills writing `$ llm prompt="..." -> R` get your impl without additional config.
|
|
126
|
+
|
|
127
|
+
## Further reading
|
|
128
|
+
|
|
129
|
+
- **[`../../../docs/configuration.md`](../../../docs/configuration.md)** — substrate selection via `connectors.json`
|
|
130
|
+
- **[`../../../docs/adopter-playbook.md`](../../../docs/adopter-playbook.md)** — programmatic-bootstrap patterns
|
|
131
|
+
- **`src/connectors/types.ts`** — authoritative `LocalModel` interface + `LocalModelManifest` curated fields
|
|
132
|
+
- **`src/connectors/local-model.ts`** — `OllamaLocalModel` reference impl
|
|
133
|
+
- **`src/connectors/local-model-mcp.ts`** — the `LocalModelMcpConnector` bridge that wraps your impl as `$ llm`
|
|
134
|
+
- **`src/testing/conformance.ts`** — the per-contract conformance test suites
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpConnectorTemplate — fork-me skeleton for writing your own McpConnector impl.
|
|
3
|
+
*
|
|
4
|
+
* This is NOT a runnable connector. Every method throws a "TODO" error. The
|
|
5
|
+
* purpose is to surface the McpConnector contract surface in a copy-and-customize
|
|
6
|
+
* shape for the exotic cases the four bundled impls don't cover.
|
|
7
|
+
*
|
|
8
|
+
* **When to fork from this template** (vs. using a bundled impl):
|
|
9
|
+
* - `RemoteMcpConnector` — covers the common case: spawn a child process,
|
|
10
|
+
* bridge stdio (LSP or newline framing). YouTrack, GitHub, Linear, most
|
|
11
|
+
* remote MCPs all wire through this. Use it for ANY external MCP server
|
|
12
|
+
* reachable via stdio bridging — JSON-configurable via `connectors.json`.
|
|
13
|
+
* - `CallbackMcpConnector` — wraps a JS function as McpConnector. For test
|
|
14
|
+
* rigs + embedder-wired transports where the dispatch is local code.
|
|
15
|
+
* - `LocalModelMcpConnector` — bridges a registered LocalModel as
|
|
16
|
+
* `$ llm prompt=...` MCP dispatch. Auto-wired when LocalModel substrate
|
|
17
|
+
* is configured.
|
|
18
|
+
* - `MemoryStoreMcpConnector` — bridges a registered MemoryStore as
|
|
19
|
+
* `$ memory mode=...` MCP dispatch. Auto-wired when MemoryStore substrate
|
|
20
|
+
* is configured.
|
|
21
|
+
*
|
|
22
|
+
* Fork from this template when none of those fit — e.g.:
|
|
23
|
+
* - Direct HTTP MCP (JSON-RPC over HTTP, no child process)
|
|
24
|
+
* - WebSocket MCP
|
|
25
|
+
* - In-process MCP (call methods directly without IPC)
|
|
26
|
+
* - Custom protocol that doesn't match stdio framing
|
|
27
|
+
* - Cross-thread / worker-pool dispatch
|
|
28
|
+
*
|
|
29
|
+
* Forking workflow:
|
|
30
|
+
* 1. Copy this directory: `cp -r examples/connectors/McpConnectorTemplate examples/connectors/MyMcpConnector`
|
|
31
|
+
* 2. Rename the class — typically `<Transport>McpConnector` (e.g., `HttpMcpConnector`, `WebSocketMcpConnector`)
|
|
32
|
+
* 3. Implement `call()` against your transport
|
|
33
|
+
* 4. Implement `manifest()` returning transport metadata
|
|
34
|
+
* 5. Update `staticCapabilities()` to declare what your impl supports
|
|
35
|
+
* 6. (Optional) Implement `staticTools()` — returns the closed-set list of
|
|
36
|
+
* tools your connector exposes so lint can validate `$ name.tool` references
|
|
37
|
+
* 7. Register from your adopter bootstrap:
|
|
38
|
+
* `registry.registerMcpConnector("mytool", new MyMcpConnector({ ... }))`
|
|
39
|
+
* 8. (Optional) For `connectors.json` JSON-instantiability, add a static
|
|
40
|
+
* `fromConfig(config)` factory + register via `registerConnectorClass()`:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { registerConnectorClass } from "skillscript-runtime/connectors";
|
|
43
|
+
* registerConnectorClass("MyMcpConnector", {
|
|
44
|
+
* ctor: MyMcpConnector,
|
|
45
|
+
* fromConfig: (cfg) => MyMcpConnector.fromConfig(cfg),
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* See `src/connectors/mcp-remote.ts` (`RemoteMcpConnector`) for the most
|
|
50
|
+
* comprehensive reference impl — stdio framing, child process lifecycle,
|
|
51
|
+
* `fromConfig` factory, timeout discipline. See `src/connectors/mcp.ts`
|
|
52
|
+
* (`CallbackMcpConnector`) for the minimal reference impl.
|
|
53
|
+
*
|
|
54
|
+
* Runtime hosts (MCP server + web dashboard) honor whichever McpConnector
|
|
55
|
+
* instances your registry has — bare `$ <name>` or qualified `$ <name>.<tool>`
|
|
56
|
+
* dispatches route through whatever's registered.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import type {
|
|
60
|
+
McpConnector,
|
|
61
|
+
McpDispatchCtx,
|
|
62
|
+
McpConnectorCapabilities,
|
|
63
|
+
ManifestInfo,
|
|
64
|
+
} from "../../../src/connectors/types.js";
|
|
65
|
+
|
|
66
|
+
/** Replace with your transport's connection config (URL, auth, timeouts, etc.). */
|
|
67
|
+
export interface McpConnectorTemplateConfig {
|
|
68
|
+
// TODO — declare the fields your transport needs to connect.
|
|
69
|
+
// Examples:
|
|
70
|
+
// httpEndpoint?: string;
|
|
71
|
+
// wsUrl?: string;
|
|
72
|
+
// authToken?: string;
|
|
73
|
+
// timeoutMs?: number;
|
|
74
|
+
exampleConfigField?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class McpConnectorTemplate implements McpConnector {
|
|
78
|
+
/**
|
|
79
|
+
* Declare what your impl supports. The runtime + lint consult these flags.
|
|
80
|
+
* Conservative defaults shown; flip to true once your impl actually supports
|
|
81
|
+
* each capability.
|
|
82
|
+
*/
|
|
83
|
+
static staticCapabilities(): McpConnectorCapabilities {
|
|
84
|
+
return {
|
|
85
|
+
connector_type: "mcp_connector",
|
|
86
|
+
implementation: "McpConnectorTemplate", // ← rename to your class name
|
|
87
|
+
contract_version: "1.0.0",
|
|
88
|
+
features: {
|
|
89
|
+
// TODO — set each flag based on what your transport can actually do.
|
|
90
|
+
supports_identity_propagation: false, // can ctxOverrides.agentId thread through to upstream?
|
|
91
|
+
supports_streaming_responses: false, // can `call()` return an async iterable?
|
|
92
|
+
supports_batch: false, // can the upstream batch multiple tool calls?
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* (Optional) Declare the closed-set tool surface for lint validation.
|
|
99
|
+
*
|
|
100
|
+
* Return:
|
|
101
|
+
* - `string[]` — the canonical tool names your connector exposes;
|
|
102
|
+
* `$ name.tool` references are validated against this list at lint time
|
|
103
|
+
* - `null` — surface varies at runtime (e.g., `RemoteMcpConnector` wraps
|
|
104
|
+
* an arbitrary upstream MCP server); lint emits a tier-3 advisory
|
|
105
|
+
* instead of green-lighting
|
|
106
|
+
*
|
|
107
|
+
* Omit this method entirely and behavior is the same as `null`.
|
|
108
|
+
*/
|
|
109
|
+
// static staticTools(): string[] | null {
|
|
110
|
+
// return ["my_tool_a", "my_tool_b"];
|
|
111
|
+
// }
|
|
112
|
+
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
114
|
+
constructor(_config: McpConnectorTemplateConfig) {
|
|
115
|
+
// TODO — establish your transport. For HTTP: store base URL + auth headers.
|
|
116
|
+
// For WebSocket: open the connection (or defer to first dispatch).
|
|
117
|
+
// For in-process: cache references to the dispatch targets.
|
|
118
|
+
throw new Error("McpConnectorTemplate is a fork-me skeleton; replace with your impl.");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* (Optional, for `connectors.json` JSON-instantiability)
|
|
123
|
+
*
|
|
124
|
+
* Factory that constructs an instance from a JSON `config` block.
|
|
125
|
+
* Used by `loadConnectorsConfig()` when adopters declare your class in
|
|
126
|
+
* `connectors.json`:
|
|
127
|
+
*
|
|
128
|
+
* { "mytool": { "class": "MyMcpConnector", "config": { ... } } }
|
|
129
|
+
*
|
|
130
|
+
* Validates the config shape; throws a clear error on missing/wrong fields.
|
|
131
|
+
*
|
|
132
|
+
* Adopters register the class via:
|
|
133
|
+
* ```
|
|
134
|
+
* import { registerConnectorClass } from "skillscript-runtime/connectors";
|
|
135
|
+
* registerConnectorClass("MyMcpConnector", {
|
|
136
|
+
* ctor: MyMcpConnector,
|
|
137
|
+
* fromConfig: (cfg) => MyMcpConnector.fromConfig(cfg),
|
|
138
|
+
* });
|
|
139
|
+
* ```
|
|
140
|
+
* BEFORE `loadConnectorsConfig` runs in their bootstrap.
|
|
141
|
+
*
|
|
142
|
+
* Omit `fromConfig` if your connector can't be configured via JSON (e.g.,
|
|
143
|
+
* it needs a runtime instance the way `CallbackMcpConnector` needs a
|
|
144
|
+
* dispatch function). Adopters then wire it via embedder code only.
|
|
145
|
+
*/
|
|
146
|
+
// static fromConfig(_config: Record<string, unknown>): McpConnectorTemplate {
|
|
147
|
+
// // TODO — validate config + construct.
|
|
148
|
+
// throw new Error("TODO: fromConfig() — validate JSON config + instantiate.");
|
|
149
|
+
// }
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Dispatch a tool call to your transport.
|
|
153
|
+
*
|
|
154
|
+
* `toolName` — the tool the caller wants (`$ youtrack.search_issues` →
|
|
155
|
+
* `toolName = "search_issues"`; bare `$ youtrack args` → `toolName` = the
|
|
156
|
+
* unqualified op name).
|
|
157
|
+
*
|
|
158
|
+
* `args` — the kwargs from the skill source (`$ youtrack.search_issues
|
|
159
|
+
* query="..." limit=10` → `{ query: "...", limit: 10 }`).
|
|
160
|
+
*
|
|
161
|
+
* `ctxOverrides` — optional identity overrides threaded through dispatch
|
|
162
|
+
* (`agentId`, `isAdmin`). Bundled connectors that honor identity propagation
|
|
163
|
+
* forward these to the upstream substrate.
|
|
164
|
+
*
|
|
165
|
+
* Return whatever the upstream MCP returns. Skills that bind via `-> R`
|
|
166
|
+
* get the raw result; skills that descend into it (`$(R.field)`) get
|
|
167
|
+
* tier-3 advisory if your impl's return shape isn't statically discoverable.
|
|
168
|
+
*
|
|
169
|
+
* On dispatch failure, throw — the runtime's op-level `(fallback: ...)`
|
|
170
|
+
* machinery surfaces it cleanly. Don't return error envelopes silently;
|
|
171
|
+
* the v0.5.0+ contract surfaces inner-tool errors via throw.
|
|
172
|
+
*/
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
174
|
+
async call(
|
|
175
|
+
_toolName: string,
|
|
176
|
+
_args: Record<string, unknown>,
|
|
177
|
+
_ctxOverrides?: McpDispatchCtx,
|
|
178
|
+
): Promise<unknown> {
|
|
179
|
+
// TODO — dispatch via your transport.
|
|
180
|
+
// - Map toolName + args to your transport's wire format
|
|
181
|
+
// - Apply ctxOverrides if you support identity propagation
|
|
182
|
+
// - Apply your transport-specific timeout
|
|
183
|
+
// - Throw on dispatch failure (don't return an error envelope silently)
|
|
184
|
+
// - Return the raw response from upstream
|
|
185
|
+
throw new Error("TODO: call() — dispatch tool call via your transport.");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Capability snapshot for `runtime_capabilities` discovery. Return free-form
|
|
190
|
+
* transport-specific metadata.
|
|
191
|
+
*
|
|
192
|
+
* The bundled `RemoteMcpConnector.manifest()` returns:
|
|
193
|
+
* { capabilities_version: "1", manifest: { kind: "remote-mcp",
|
|
194
|
+
* command: "...", framing: "lsp", tools_available: [...] } }
|
|
195
|
+
*/
|
|
196
|
+
async manifest(): Promise<ManifestInfo<"mcp_connector">> {
|
|
197
|
+
// TODO — return a snapshot of your transport's metadata.
|
|
198
|
+
throw new Error("TODO: manifest() — return transport-specific capability snapshot.");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# McpConnectorTemplate — fork-me skeleton
|
|
2
|
+
|
|
3
|
+
A skeleton `McpConnector` 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
|
+
|
|
5
|
+
**Most adopters don't need this template.** Four bundled impls already cover the common cases:
|
|
6
|
+
|
|
7
|
+
| Bundled impl | What it covers |
|
|
8
|
+
|---|---|
|
|
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
|
+
| `CallbackMcpConnector` | Wraps a JS function. Test rigs + embedder-wired transports where the dispatch is local code. |
|
|
11
|
+
| `LocalModelMcpConnector` | Bridges a registered `LocalModel` as `$ llm prompt=...`. Auto-wired when `substrate.local_model` is set. |
|
|
12
|
+
| `MemoryStoreMcpConnector` | Bridges a registered `MemoryStore` as `$ memory mode=...`. Auto-wired when `substrate.memory_store` is set. |
|
|
13
|
+
|
|
14
|
+
**Fork this template only when none of those fit** — e.g.:
|
|
15
|
+
|
|
16
|
+
- Direct HTTP MCP (JSON-RPC over HTTP, no child process)
|
|
17
|
+
- WebSocket MCP
|
|
18
|
+
- In-process MCP (call methods directly without IPC)
|
|
19
|
+
- Custom transport that doesn't match stdio framing
|
|
20
|
+
- Cross-thread / worker-pool dispatch
|
|
21
|
+
|
|
22
|
+
If you're trying to wire a remote MCP server like YouTrack or GitHub, **you want `RemoteMcpConnector` in `connectors.json`**, not this template. See `connectors.json.example` for the wiring pattern.
|
|
23
|
+
|
|
24
|
+
## Forking workflow
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cp -r examples/connectors/McpConnectorTemplate examples/connectors/MyMcpConnector
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
1. **Rename the class.** Convention: `<Transport>McpConnector` (e.g., `HttpMcpConnector`, `WebSocketMcpConnector`, `InProcessMcpConnector`).
|
|
31
|
+
2. **Define your config interface.** Edit `McpConnectorTemplateConfig` to declare what your transport needs (URL, auth, timeouts, etc.).
|
|
32
|
+
3. **Implement `call()`** — translate `(toolName, args)` to your transport's wire format; dispatch; return the result.
|
|
33
|
+
4. **Implement `manifest()`** — return transport metadata for `runtime_capabilities` discovery.
|
|
34
|
+
5. **Update `staticCapabilities()`** to declare what your impl supports.
|
|
35
|
+
6. **(Optional) Implement `staticTools()`** — return the closed-set list of tools your connector exposes if static; lint validates `$ name.tool` against this list. Return `null` (or omit) if the surface is runtime-discovered.
|
|
36
|
+
7. **Wire from your adopter bootstrap:**
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Registry } from "skillscript-runtime";
|
|
40
|
+
import { MyMcpConnector } from "./MyMcpConnector.js";
|
|
41
|
+
|
|
42
|
+
const registry = new Registry();
|
|
43
|
+
registry.registerMcpConnector("mytool", new MyMcpConnector({ /* config */ }));
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
8. **(Optional) For `connectors.json` JSON-instantiability** — add a static `fromConfig(config)` factory + register the class via `registerConnectorClass()`:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { registerConnectorClass } from "skillscript-runtime/connectors";
|
|
50
|
+
|
|
51
|
+
registerConnectorClass("MyMcpConnector", {
|
|
52
|
+
ctor: MyMcpConnector,
|
|
53
|
+
fromConfig: (cfg) => MyMcpConnector.fromConfig(cfg),
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Call this BEFORE `loadConnectorsConfig` runs in your bootstrap. Then adopters can declare instances in `connectors.json`:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"myinstance": {
|
|
62
|
+
"class": "MyMcpConnector",
|
|
63
|
+
"config": { "endpoint": "https://...", "authToken": "${MY_TOKEN}" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Reference implementations
|
|
69
|
+
|
|
70
|
+
The bundled impls are the canonical reference:
|
|
71
|
+
|
|
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
|
+
- **`src/connectors/mcp.ts`** — `CallbackMcpConnector`. Minimal reference: 60 LOC. The closest reference for embedder-wired transports.
|
|
74
|
+
|
|
75
|
+
When in doubt, read both + the bridge classes (`local-model-mcp.ts`, `memory-store-mcp.ts`) for how single-substrate bridges work.
|
|
76
|
+
|
|
77
|
+
## Contract surface (2 methods)
|
|
78
|
+
|
|
79
|
+
McpConnector is the narrowest of the five contracts:
|
|
80
|
+
|
|
81
|
+
| Method | What it does | When called |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `call(toolName, args, ctx?)` | Dispatch a tool call to your transport | Every `$ <connector> ...` op |
|
|
84
|
+
| `manifest()` | Transport metadata for discovery | At startup + on-demand from MCP clients |
|
|
85
|
+
|
|
86
|
+
Plus `staticCapabilities()` (required static) and `staticTools()` (optional static).
|
|
87
|
+
|
|
88
|
+
### `call()` semantics
|
|
89
|
+
|
|
90
|
+
- **`toolName`** — the tool the caller wants:
|
|
91
|
+
- `$ youtrack.search_issues query="..."` → `toolName = "search_issues"`
|
|
92
|
+
- `$ youtrack query="..."` (bare form) → `toolName` is the *unqualified op name* (typically the connector's auto-routing target)
|
|
93
|
+
- **`args`** — kwargs from the skill source as a plain object
|
|
94
|
+
- **`ctxOverrides`** — optional identity propagation (`agentId`, `isAdmin`). Connectors that support identity-propagation thread these to upstream
|
|
95
|
+
- **Return** — whatever the upstream MCP returns. Skills bind via `-> R`; if the return shape isn't statically discoverable, lint emits a tier-3 advisory when callers descend (`$(R.field)`)
|
|
96
|
+
|
|
97
|
+
On dispatch failure, **throw** — the runtime's op-level `(fallback: ...)` machinery handles it cleanly. Don't return error envelopes silently; the v0.5.0+ contract surfaces inner-tool errors via throw.
|
|
98
|
+
|
|
99
|
+
### `staticTools()` lint integration
|
|
100
|
+
|
|
101
|
+
If your connector exposes a known closed set of tools (e.g., a JIRA wrapper that only exposes `search_issues`, `get_issue`, `create_issue`), implementing `staticTools()` lets lint validate `$ name.tool` references at authoring time:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
static staticTools(): string[] {
|
|
105
|
+
return ["search_issues", "get_issue", "create_issue"];
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Skills that write `$ jira.unknown_tool ...` fail lint with `unknown-tool-on-connector` (tier-1 error).
|
|
110
|
+
|
|
111
|
+
If your surface is runtime-discovered (e.g., `RemoteMcpConnector` wrapping an arbitrary upstream MCP), return `null` or omit the method. Lint emits a tier-3 advisory on dotted dispatch instead of green-lighting.
|
|
112
|
+
|
|
113
|
+
## Wiring against the dashboard / MCP
|
|
114
|
+
|
|
115
|
+
Runtime hosts (MCP server + web dashboard) honor whichever McpConnector instances your registry has. There's no `substrate` slot for McpConnector — that's intentional, because McpConnector is intrinsically *instanced* (you'll have multiple: `youtrack`, `github`, `jira`, ...) not singleton.
|
|
116
|
+
|
|
117
|
+
Two registration paths:
|
|
118
|
+
|
|
119
|
+
- **Programmatic** (your bootstrap):
|
|
120
|
+
```typescript
|
|
121
|
+
registry.registerMcpConnector("youtrack", new RemoteMcpConnector({ command: "npx", args: ["mcp-remote", "..."] }));
|
|
122
|
+
registry.registerMcpConnector("github", new RemoteMcpConnector({ ... }));
|
|
123
|
+
```
|
|
124
|
+
- **Declarative** (`connectors.json`):
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"youtrack": { "class": "RemoteMcpConnector", "config": { "command": "npx", ... } },
|
|
128
|
+
"github": { "class": "RemoteMcpConnector", "config": { ... } }
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
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
|
+
|
|
134
|
+
## McpConnector vs. SkillStore / MemoryStore differences
|
|
135
|
+
|
|
136
|
+
| Aspect | McpConnector | SkillStore / MemoryStore |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| Methods | 2 (call, manifest) | 3-8 (more state machine) |
|
|
139
|
+
| Cardinality | Many instances per deployment | One singleton per slot |
|
|
140
|
+
| Substrate config | Per-instance via top-level keys | `substrate` section short/object/custom |
|
|
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` + `memory` + `memory_write` (LocalModel + MemoryStore exposed via bridge connectors) | n/a (these ARE the substrates being bridged) |
|
|
143
|
+
|
|
144
|
+
McpConnector is fundamentally the "dispatch to external tools" surface — narrowest contract, broadest range of impls.
|
|
145
|
+
|
|
146
|
+
## Further reading
|
|
147
|
+
|
|
148
|
+
- **[`../../../docs/configuration.md`](../../../docs/configuration.md)** — `connectors.json` wiring (substrate + named MCP instances)
|
|
149
|
+
- **[`../../../docs/adopter-playbook.md`](../../../docs/adopter-playbook.md)** — Case 1 vs Case 2 wiring patterns; substrate-portable vs substrate-locked
|
|
150
|
+
- **`src/connectors/types.ts`** — authoritative `McpConnector` interface
|
|
151
|
+
- **`src/connectors/mcp-remote.ts`** — full-featured reference impl with stdio framing + lifecycle
|
|
152
|
+
- **`src/connectors/mcp.ts`** — minimal reference impl
|
|
153
|
+
- **`src/connectors/config.ts`** — `registerConnectorClass()` + `loadConnectorsConfig()`
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryStoreTemplate — fork-me skeleton for writing your own MemoryStore impl.
|
|
3
|
+
*
|
|
4
|
+
* This is NOT a runnable connector. Every method throws a "TODO" error. The
|
|
5
|
+
* purpose is to surface the MemoryStore contract surface in a copy-and-customize
|
|
6
|
+
* shape so adopters writing AMP-, Pinecone-, Weaviate-, Qdrant-, or
|
|
7
|
+
* Postgres-backed MemoryStores have a starting skeleton.
|
|
8
|
+
*
|
|
9
|
+
* Forking workflow:
|
|
10
|
+
* 1. Copy this directory into your codebase (`cp -r examples/connectors/MemoryStoreTemplate examples/connectors/MyMemoryStore`)
|
|
11
|
+
* 2. Rename the class — typically `<Substrate>MemoryStore` (e.g., `PineconeMemoryStore`, `AmpMemoryStore`)
|
|
12
|
+
* 3. Implement each method against your substrate's API
|
|
13
|
+
* 4. Update `staticCapabilities()` to declare what your impl actually supports
|
|
14
|
+
* 5. Register from your adopter bootstrap:
|
|
15
|
+
* `registry.registerMemoryStore("primary", new MyMemoryStore({ ... }))`
|
|
16
|
+
* 6. Validate via the conformance suite:
|
|
17
|
+
* `MemoryStoreConformance.buildTests({ build: () => new MyMemoryStore(...), ctor: MyMemoryStore })`
|
|
18
|
+
*
|
|
19
|
+
* See `src/connectors/memory-store.ts` for the working reference implementation
|
|
20
|
+
* (`SqliteMemoryStore` — SQLite + FTS5 backing). The full contract spec lives in
|
|
21
|
+
* `src/connectors/types.ts` (`MemoryStore` interface + `PortableMemory` +
|
|
22
|
+
* `QueryFilters` + `MemoryWrite` types).
|
|
23
|
+
*
|
|
24
|
+
* Runtime hosts (MCP server + web dashboard) honor whichever MemoryStore impl
|
|
25
|
+
* you register via the registry, so once your fork passes the conformance
|
|
26
|
+
* suite the entire `$ memory` / `$ memory_write` dispatch path reads + writes
|
|
27
|
+
* against your substrate.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
MemoryStore,
|
|
32
|
+
QueryFilters,
|
|
33
|
+
PortableMemory,
|
|
34
|
+
MemoryWrite,
|
|
35
|
+
MemoryWriteRecord,
|
|
36
|
+
MemoryStoreCapabilities,
|
|
37
|
+
ManifestInfo,
|
|
38
|
+
} from "../../../src/connectors/types.js";
|
|
39
|
+
|
|
40
|
+
/** Replace with your substrate's connection config (host, dbName, API key, etc.). */
|
|
41
|
+
export interface MemoryStoreTemplateConfig {
|
|
42
|
+
// TODO — declare the fields your substrate needs to connect.
|
|
43
|
+
// Examples:
|
|
44
|
+
// pineconeApiKey?: string;
|
|
45
|
+
// pineconeEnvironment?: string;
|
|
46
|
+
// weaviateUrl?: string;
|
|
47
|
+
// ampVault?: string;
|
|
48
|
+
exampleConfigField?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class MemoryStoreTemplate implements MemoryStore {
|
|
52
|
+
/**
|
|
53
|
+
* Declare what your impl supports. The runtime + lint consult these flags
|
|
54
|
+
* before exercising features. Set conservatively — overclaiming triggers
|
|
55
|
+
* cryptic downstream failures; underclaiming hides usable features.
|
|
56
|
+
*/
|
|
57
|
+
static staticCapabilities(): MemoryStoreCapabilities {
|
|
58
|
+
return {
|
|
59
|
+
connector_type: "memory_store",
|
|
60
|
+
implementation: "MemoryStoreTemplate", // ← rename to your class name
|
|
61
|
+
contract_version: "1.0.0",
|
|
62
|
+
features: {
|
|
63
|
+
// TODO — set each flag based on what your substrate can actually do.
|
|
64
|
+
supports_writes: false, // can write() persist new memories?
|
|
65
|
+
supports_tag_filter: false, // can query() filter by domain_tags?
|
|
66
|
+
supports_semantic: false, // mode="semantic" supported?
|
|
67
|
+
supports_rerank: false, // mode="rerank" supported?
|
|
68
|
+
supports_thread_status_filter: false, // can query() filter by thread_status?
|
|
69
|
+
supports_pinning: false, // does the substrate track pinned: true?
|
|
70
|
+
supports_decay_model: false, // does the substrate honor decay_model?
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
76
|
+
constructor(_config: MemoryStoreTemplateConfig) {
|
|
77
|
+
// TODO — establish your substrate connection. For SQL: open the database
|
|
78
|
+
// + run schema migrations. For vector DB: cache the SDK client + index
|
|
79
|
+
// name. For HTTP: store the base URL + auth headers.
|
|
80
|
+
throw new Error("MemoryStoreTemplate is a fork-me skeleton; replace with your impl.");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Capability snapshot for `runtime_capabilities` discovery. Return free-form
|
|
85
|
+
* substrate-specific metadata (kind, version, supported modes, score range, etc.).
|
|
86
|
+
*
|
|
87
|
+
* The bundled `SqliteMemoryStore.manifest()` returns:
|
|
88
|
+
* { capabilities_version: "1", manifest: { kind: "sqlite-fts",
|
|
89
|
+
* supported_modes: ["fts"], score_range: "unbounded",
|
|
90
|
+
* supported_filters: ["domain_tags"], supports_write: true } }
|
|
91
|
+
*/
|
|
92
|
+
async manifest(): Promise<ManifestInfo<"memory_store">> {
|
|
93
|
+
// TODO — return a snapshot of your substrate's capabilities.
|
|
94
|
+
throw new Error("TODO: manifest() — return substrate-specific capability snapshot.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Query memories by mode + filter. Return `PortableMemory[]` ordered by
|
|
99
|
+
* relevance (most relevant first). Empty result is fine; never throw "not
|
|
100
|
+
* found" — return [].
|
|
101
|
+
*
|
|
102
|
+
* `QueryFilters` shape:
|
|
103
|
+
* - `query`: string (search terms; substrate-specific interpretation)
|
|
104
|
+
* - `limit`: number (max results)
|
|
105
|
+
* - `mode`: "fts" | "semantic" | "rerank" | substrate-specific string
|
|
106
|
+
* - Plus arbitrary additional filter fields (`domain_tags`, `thread_status`,
|
|
107
|
+
* `payload_type`, `pinned`, `agent_id`, etc.) — substrate honors what
|
|
108
|
+
* it supports, ignores the rest. Per the curated-subset framing in
|
|
109
|
+
* `types.ts`, these top-level fields are first-class for substrates
|
|
110
|
+
* that have them.
|
|
111
|
+
*
|
|
112
|
+
* `PortableMemory` core fields:
|
|
113
|
+
* - Always: `id`, `summary`, `created_at`
|
|
114
|
+
* - Often: `detail`, `score`, `domain_tags`, `payload_type`
|
|
115
|
+
* - Per-substrate (curated): `pinned`, `confidence`, `thread_status`,
|
|
116
|
+
* `recipients`, `expires_at`, `agent_id`, `vault`
|
|
117
|
+
* - Catch-all: `metadata` object for substrate-specific extensions
|
|
118
|
+
*/
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
120
|
+
async query(_filters: QueryFilters): Promise<PortableMemory[]> {
|
|
121
|
+
// TODO — translate `filters` to your substrate's query API.
|
|
122
|
+
// - Reject unsupported `mode` values with a clear error (don't silently
|
|
123
|
+
// fall back; cold authors will be confused)
|
|
124
|
+
// - Apply curated-subset filters (domain_tags, payload_type, etc.) where
|
|
125
|
+
// the substrate supports them
|
|
126
|
+
// - Map your substrate's result rows into `PortableMemory` shape
|
|
127
|
+
// - Order by relevance (most relevant first)
|
|
128
|
+
// - Honor `limit`
|
|
129
|
+
throw new Error("TODO: query() — return PortableMemory[] ordered by relevance.");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Persist a new memory entry. Return the substrate-assigned `id` +
|
|
134
|
+
* `created_at` (unix seconds).
|
|
135
|
+
*
|
|
136
|
+
* `MemoryWrite` shape:
|
|
137
|
+
* - `content`: string (required; the memory body)
|
|
138
|
+
* - `tags`: string[] (optional; routed to substrate's tag mechanism)
|
|
139
|
+
* - `recipients`: string[] (optional advisory — substrates with alerting
|
|
140
|
+
* machinery use this to route notifications, e.g., AMP's mailbox model)
|
|
141
|
+
* - `expires_at`: number (optional; unix seconds — substrate-side TTL)
|
|
142
|
+
* - `metadata`: Record<string, unknown> (catch-all for substrate-specific
|
|
143
|
+
* extensions like `vault`, `payload_type`, `confidence`)
|
|
144
|
+
*/
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
146
|
+
async write(_entry: MemoryWrite): Promise<MemoryWriteRecord> {
|
|
147
|
+
// TODO — persist the memory via your substrate.
|
|
148
|
+
// - Generate or accept an id (substrate-dependent)
|
|
149
|
+
// - Persist content + tags + metadata
|
|
150
|
+
// - Apply recipients hint if your substrate has alerting
|
|
151
|
+
// - Apply expires_at if your substrate has TTL
|
|
152
|
+
// - Return { id, created_at }
|
|
153
|
+
throw new Error("TODO: write() — persist memory; return { id, created_at }.");
|
|
154
|
+
}
|
|
155
|
+
}
|