skillscript-runtime 0.9.9 → 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.
Files changed (92) hide show
  1. package/README.md +39 -46
  2. package/dist/bootstrap.d.ts +57 -18
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +188 -54
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/cli.js +5 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/connectors/agent-noop.d.ts +2 -2
  9. package/dist/connectors/agent-noop.d.ts.map +1 -1
  10. package/dist/connectors/config.d.ts +46 -0
  11. package/dist/connectors/config.d.ts.map +1 -1
  12. package/dist/connectors/config.js +109 -1
  13. package/dist/connectors/config.js.map +1 -1
  14. package/dist/connectors/index.d.ts +3 -1
  15. package/dist/connectors/index.d.ts.map +1 -1
  16. package/dist/connectors/index.js +6 -3
  17. package/dist/connectors/index.js.map +1 -1
  18. package/dist/connectors/local-model-mcp.d.ts +3 -3
  19. package/dist/connectors/local-model-mcp.d.ts.map +1 -1
  20. package/dist/connectors/local-model.d.ts +30 -11
  21. package/dist/connectors/local-model.d.ts.map +1 -1
  22. package/dist/connectors/local-model.js +44 -12
  23. package/dist/connectors/local-model.js.map +1 -1
  24. package/dist/connectors/mcp-remote.d.ts +3 -3
  25. package/dist/connectors/mcp-remote.d.ts.map +1 -1
  26. package/dist/connectors/mcp.d.ts +3 -3
  27. package/dist/connectors/mcp.d.ts.map +1 -1
  28. package/dist/connectors/memory-store-mcp.d.ts +3 -3
  29. package/dist/connectors/memory-store-mcp.d.ts.map +1 -1
  30. package/dist/connectors/memory-store-mcp.js +3 -1
  31. package/dist/connectors/memory-store-mcp.js.map +1 -1
  32. package/dist/connectors/memory-store.d.ts +26 -6
  33. package/dist/connectors/memory-store.d.ts.map +1 -1
  34. package/dist/connectors/memory-store.js +84 -15
  35. package/dist/connectors/memory-store.js.map +1 -1
  36. package/dist/connectors/registry.d.ts +28 -5
  37. package/dist/connectors/registry.d.ts.map +1 -1
  38. package/dist/connectors/registry.js +34 -11
  39. package/dist/connectors/registry.js.map +1 -1
  40. package/dist/connectors/skill-store.d.ts +3 -3
  41. package/dist/connectors/skill-store.d.ts.map +1 -1
  42. package/dist/connectors/sqlite-skill-store.d.ts +89 -0
  43. package/dist/connectors/sqlite-skill-store.d.ts.map +1 -0
  44. package/dist/connectors/sqlite-skill-store.js +506 -0
  45. package/dist/connectors/sqlite-skill-store.js.map +1 -0
  46. package/dist/connectors/types.d.ts +86 -9
  47. package/dist/connectors/types.d.ts.map +1 -1
  48. package/dist/connectors/types.js.map +1 -1
  49. package/dist/errors.d.ts +15 -1
  50. package/dist/errors.d.ts.map +1 -1
  51. package/dist/errors.js +47 -6
  52. package/dist/errors.js.map +1 -1
  53. package/dist/help-content.d.ts.map +1 -1
  54. package/dist/help-content.js +18 -4
  55. package/dist/help-content.js.map +1 -1
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +1 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/runtime-config.d.ts +0 -2
  61. package/dist/runtime-config.d.ts.map +1 -1
  62. package/dist/runtime-config.js +1 -1
  63. package/dist/runtime-config.js.map +1 -1
  64. package/dist/runtime.d.ts.map +1 -1
  65. package/dist/runtime.js +29 -30
  66. package/dist/runtime.js.map +1 -1
  67. package/dist/testing/conformance.d.ts.map +1 -1
  68. package/dist/testing/conformance.js +12 -6
  69. package/dist/testing/conformance.js.map +1 -1
  70. package/examples/README.md +13 -13
  71. package/examples/connectors/LocalModelTemplate/LocalModelTemplate.ts +138 -0
  72. package/examples/connectors/LocalModelTemplate/README.md +134 -0
  73. package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +200 -0
  74. package/examples/connectors/McpConnectorTemplate/README.md +153 -0
  75. package/examples/connectors/MemoryStoreTemplate/MemoryStoreTemplate.ts +155 -0
  76. package/examples/connectors/MemoryStoreTemplate/README.md +169 -0
  77. package/examples/connectors/README.md +47 -0
  78. package/examples/connectors/SkillStoreTemplate/README.md +143 -0
  79. package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +199 -0
  80. package/examples/{hello.skill.provenance.json → skillscripts/hello.skill.provenance.json} +1 -1
  81. package/package.json +1 -1
  82. package/scaffold/config.toml +8 -12
  83. package/scaffold/connectors.json +31 -3
  84. /package/examples/{classify-support-ticket.skill.md → skillscripts/classify-support-ticket.skill.md} +0 -0
  85. /package/examples/{cut-release-tag.skill.md → skillscripts/cut-release-tag.skill.md} +0 -0
  86. /package/examples/{doc-qa-with-citations.skill.md → skillscripts/doc-qa-with-citations.skill.md} +0 -0
  87. /package/examples/{feedback-sentiment-scan.skill.md → skillscripts/feedback-sentiment-scan.skill.md} +0 -0
  88. /package/examples/{hello.skill.md → skillscripts/hello.skill.md} +0 -0
  89. /package/examples/{morning-brief.skill.md → skillscripts/morning-brief.skill.md} +0 -0
  90. /package/examples/{queue-length-monitor.skill.md → skillscripts/queue-length-monitor.skill.md} +0 -0
  91. /package/examples/{service-health-watch.skill.md → skillscripts/service-health-watch.skill.md} +0 -0
  92. /package/examples/{youtrack-morning-sweep.skill.md → skillscripts/youtrack-morning-sweep.skill.md} +0 -0
@@ -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
+ }
@@ -0,0 +1,169 @@
1
+ # MemoryStoreTemplate — fork-me skeleton
2
+
3
+ A skeleton `MemoryStore` 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 skillscript memories backed by:
6
+ - A vector database (Pinecone, Weaviate, Qdrant, Chroma)
7
+ - A memory system (AMP, hosted memory API)
8
+ - A different SQL flavor (Postgres + pgvector, MySQL)
9
+ - An HTTP CRUD service
10
+ - Anything else with query + write semantics
11
+
12
+ ## The three legs
13
+
14
+ ```
15
+ MemoryStore (choose which connector)
16
+ ├── SqliteMemoryStore (bundled, SQLite + FTS5 — src/connectors/memory-store.ts)
17
+ ├── (no other bundled impl — vector DBs and remote services are adopter-side)
18
+ └── Your fork from this template
19
+ ```
20
+
21
+ The runtime is substrate-agnostic. Memories don't know which backend they're stored against — the contract is the `MemoryStore` interface, and any class implementing it works.
22
+
23
+ ## Forking workflow
24
+
25
+ ```bash
26
+ cp -r examples/connectors/MemoryStoreTemplate examples/connectors/MyMemoryStore
27
+ ```
28
+
29
+ 1. **Rename the class.** Convention: `<Substrate>MemoryStore` (e.g., `PineconeMemoryStore`, `PostgresMemoryStore`, `AmpMemoryStore`).
30
+ 2. **Define your config interface.** Edit `MemoryStoreTemplateConfig` to declare what your substrate needs (connection URL, API key, vault name, index name, etc.).
31
+ 3. **Implement each method.** Three methods + `staticCapabilities()`. Each has a TODO comment in the skeleton explaining what to do.
32
+ 4. **Update `staticCapabilities()`** to declare what your impl actually supports. The runtime + lint consult these flags before exercising features.
33
+ 5. **Wire from your adopter bootstrap:**
34
+
35
+ ```typescript
36
+ import { Registry } from "skillscript-runtime";
37
+ import { MyMemoryStore } from "./MyMemoryStore.js";
38
+
39
+ const registry = new Registry();
40
+ registry.registerMemoryStore("primary", new MyMemoryStore({
41
+ // your config
42
+ }));
43
+ ```
44
+
45
+ 6. **Validate via the conformance suite:**
46
+
47
+ ```typescript
48
+ import { describe, it } from "vitest";
49
+ import { MemoryStoreConformance } from "skillscript-runtime/testing";
50
+ import { MyMemoryStore } from "./MyMemoryStore.js";
51
+
52
+ describe("MyMemoryStore conformance", () => {
53
+ const tests = MemoryStoreConformance.buildTests({
54
+ build: () => new MyMemoryStore({ /* test config */ }),
55
+ ctor: MyMemoryStore,
56
+ });
57
+ for (const t of tests) it(`[${t.category}] ${t.name}`, t.run);
58
+ });
59
+ ```
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 `SqliteMemoryStore`.
62
+
63
+ ## Reference implementation
64
+
65
+ When in doubt about semantics, read the bundled impl:
66
+
67
+ - **`src/connectors/memory-store.ts`** — `SqliteMemoryStore`, 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
+
69
+ ## Contract surface (3 methods)
70
+
71
+ The MemoryStore contract is narrower than SkillStore. Three methods + `staticCapabilities()`:
72
+
73
+ | Method | What it does | When called |
74
+ |---|---|---|
75
+ | `query(filters)` | Read memories by mode + filter; return `PortableMemory[]` | Every `$ memory mode=... query=...` op |
76
+ | `write(entry)` | Persist a new memory; return `{id, created_at}` | Every `$ memory_write content=... -> R` op + `memory_write` skill notify routes |
77
+ | `manifest()` | Capability snapshot for `runtime_capabilities` discovery | At startup + on-demand from MCP clients |
78
+
79
+ Per the curated-subset framing in `src/connectors/types.ts`, `PortableMemory` has a 4-tier field model:
80
+
81
+ 1. **Core fields** — `id`, `summary`, `detail`, `score` (always-meaningful)
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`
83
+ 3. **Substrate-specific** — accessed via `metadata.X` (catch-all)
84
+ 4. **Ambient passthrough** — literal `$(MEMORY.field)` references for unknowns
85
+
86
+ **Connector duplication discipline**: a curated-subset field must be at top-level only, never also in `metadata`. Silent divergence otherwise.
87
+
88
+ ## Query modes
89
+
90
+ `QueryFilters.mode` is the dispatch axis:
91
+
92
+ - **`"fts"`** — full-text search; substrate-defined (BM25, BM25F, etc.). `SqliteMemoryStore` supports this via FTS5.
93
+ - **`"semantic"`** — embedding-based similarity. Vector-DB substrates support this; `SqliteMemoryStore` does not.
94
+ - **`"rerank"`** — substrate-defined hybrid (e.g., FTS retrieve + embedding rerank). Optional.
95
+ - **substrate-specific strings** — your fork can define its own modes; document them in `manifest().manifest.supported_modes`.
96
+
97
+ Throw a clear error on unsupported modes (don't silently fall back; cold authors will be confused).
98
+
99
+ ## Filter fields
100
+
101
+ `QueryFilters` extends a base shape with arbitrary additional filter keys. Common filters substrates honor:
102
+
103
+ - **`domain_tags`** — string or array; substrate-specific match semantics (substring vs exact; AND vs OR)
104
+ - **`payload_type`** — usually exact match
105
+ - **`thread_status`** — usually exact match (set if your substrate has thread lifecycle)
106
+ - **`pinned`** — boolean
107
+ - **`agent_id`** — adopter-side scoping
108
+ - **`recipients`** — array match (any-of)
109
+
110
+ Honor what your substrate supports; ignore the rest. Document the supported set in `manifest().manifest.supported_filters`.
111
+
112
+ ## `write()` semantics
113
+
114
+ The `MemoryWrite` shape:
115
+
116
+ - **`content`** (required) — the memory body
117
+ - **`tags`** (optional) — routed to your substrate's tag mechanism
118
+ - **`recipients`** (optional) — advisory hint; substrates with alerting (e.g., AMP's mailbox model) use it
119
+ - **`expires_at`** (optional) — unix seconds; substrates with TTL honor it
120
+ - **`metadata`** (optional) — catch-all for substrate extensions (`vault`, `payload_type`, `confidence`, etc.)
121
+
122
+ Return `{id, created_at}` — the substrate-assigned identifier + creation timestamp (unix seconds).
123
+
124
+ If your substrate doesn't support writes (read-only memory 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
+
126
+ ## Wiring against the dashboard / MCP
127
+
128
+ Runtime hosts honor whichever MemoryStore impl your registry has. To make your fork visible through `skillfile dashboard`:
129
+
130
+ - Write a custom bootstrap that constructs the runtime with your MemoryStore (see `src/bootstrap.ts` for the reference shape)
131
+ - OR (planned future) declare in `~/.skillscript/connectors.json`:
132
+
133
+ ```json
134
+ {
135
+ "substrate": {
136
+ "memory_store": {
137
+ "type": "custom",
138
+ "module": "./my-memory-store.js",
139
+ "export": "MyMemoryStore",
140
+ "config": { "pineconeApiKey": "${PINECONE_KEY}" }
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
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
+
148
+ ## MemoryStore vs SkillStore differences
149
+
150
+ Both are substrate-agnostic, but they differ in shape:
151
+
152
+ | Aspect | SkillStore | MemoryStore |
153
+ |---|---|---|
154
+ | Methods | 8 (load, query, metadata, versions, store, delete, update_status, manifest) | 3 (query, write, manifest) |
155
+ | Versioning | First-class (`versions()`, `load(name, version)`) | Substrate-side (e.g., `supersededById` in AMP); not in contract |
156
+ | Status lifecycle | Draft/Approved/Disabled with approval-token stamping | None |
157
+ | Mutation gates | Approved transitions require approval token | None — writes are append-only |
158
+ | Curated-subset fields | None | Many (`domain_tags`, `pinned`, `confidence`, `recipients`, etc.) |
159
+ | Forking complexity | Higher (multi-method state machine) | Lower (three methods, simpler semantics) |
160
+
161
+ The narrower contract is why MemoryStoreTemplate ships sooner + smaller than SkillStoreTemplate.
162
+
163
+ ## Further reading
164
+
165
+ - **[`../../../docs/configuration.md`](../../../docs/configuration.md)** — substrate selection via `connectors.json`
166
+ - **[`../../../docs/adopter-playbook.md`](../../../docs/adopter-playbook.md)** — programmatic-bootstrap patterns; two-instance posture
167
+ - **`src/connectors/types.ts`** — authoritative `MemoryStore` interface, `PortableMemory`, `QueryFilters`, `MemoryWrite` types
168
+ - **`src/testing/conformance.ts`** — the per-contract conformance test suites
169
+ - **`src/connectors/memory-store.ts`** — `SqliteMemoryStore` reference impl + schema
@@ -0,0 +1,47 @@
1
+ # examples/connectors/
2
+
3
+ Worked examples + fork-me templates for adopter-written connectors. The bundled defaults live in `src/connectors/`; this directory holds patterns for adopters writing their own.
4
+
5
+ ## Connector landscape
6
+
7
+ | Contract | Bundled defaults | Worked example | Fork template |
8
+ |---|---|---|---|
9
+ | `SkillStore` | `FilesystemSkillStore`, `SqliteSkillStore` (in `src/connectors/`) | — | **[SkillStoreTemplate/](./SkillStoreTemplate/)** |
10
+ | `MemoryStore` | `SqliteMemoryStore` (in `src/connectors/`) | — | **[MemoryStoreTemplate/](./MemoryStoreTemplate/)** |
11
+ | `LocalModel` | `OllamaLocalModel` (in `src/connectors/`; opt-in via substrate config) | — | **[LocalModelTemplate/](./LocalModelTemplate/)** |
12
+ | `McpConnector` | `RemoteMcpConnector`, `CallbackMcpConnector`, `LocalModelMcpConnector`, `MemoryStoreMcpConnector` (in `src/connectors/`) | — | **[McpConnectorTemplate/](./McpConnectorTemplate/)** |
13
+ | `AgentConnector` | `NoOpAgentConnector` (in `src/connectors/`) | **[HttpWebhookAgentConnector/](./HttpWebhookAgentConnector/)** | — |
14
+
15
+ **Bundled defaults** are runnable out of the box — wired through `connectors.json` substrate config or programmatic bootstrap.
16
+
17
+ **Worked examples** are real implementations for substrates that aren't bundled — copy + customize for your specific deployment. HttpWebhookAgentConnector demonstrates the AgentConnector contract against a generic HTTP-webhook substrate.
18
+
19
+ **Fork templates** are skeletons (every method throws TODO). Useful when you want the bare contract surface without any specific substrate assumptions. SkillStoreTemplate is the starting point for Postgres-, MongoDB-, AMP-, or vector-DB-backed SkillStore impls.
20
+
21
+ ## Forking workflow (any connector type)
22
+
23
+ 1. **Pick your starting point**:
24
+ - Fork from a worked example if it's close to your substrate (e.g., another HTTP service → fork HttpWebhookAgentConnector)
25
+ - Fork from a template if you're targeting a substrate type not yet demonstrated (e.g., Postgres SkillStore → fork SkillStoreTemplate)
26
+ - Fork from `src/connectors/` if you want the bundled default's behavior as a starting point (e.g., another SQL flavor → adapt sqlite-skill-store.ts)
27
+ 2. **Copy the directory** into your codebase
28
+ 3. **Implement against your substrate** (each method has a TODO comment)
29
+ 4. **Update `staticCapabilities()`** to declare what your impl actually supports
30
+ 5. **Register via `Registry.register*`** from your bootstrap
31
+ 6. **Validate via the conformance suite** (`<Contract>Conformance.buildTests()` from `skillscript-runtime/testing`)
32
+
33
+ ## Reference materials
34
+
35
+ - **[`docs/configuration.md`](../../docs/configuration.md)** — substrate selection via `connectors.json`; programmatic-bootstrap vs declarative patterns
36
+ - **[`docs/adopter-playbook.md`](../../docs/adopter-playbook.md)** — Case 1 vs Case 2 wiring decisions; two-instance posture; upstream-merge-friendly conventions
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, MemoryStore, LocalModel, McpConnector)
39
+ - **`src/testing/conformance.ts`** — the per-contract conformance test suites
40
+
41
+ ## Naming convention
42
+
43
+ Bundled defaults in `src/connectors/`: `<Substrate><Contract>` (e.g., `FilesystemSkillStore`, `SqliteSkillStore`, `OllamaLocalModel`).
44
+
45
+ Worked examples + adopter forks in `examples/connectors/`: same `<Substrate><Contract>` pattern (e.g., `HttpWebhookAgentConnector`, your `PostgresSkillStore`).
46
+
47
+ Templates: `<Contract>Template` (e.g., `SkillStoreTemplate`).