skillscript-runtime 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/approval.d.ts +9 -0
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +35 -2
- package/dist/approval.js.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +10 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +10 -6
- package/dist/cli.js.map +1 -1
- package/dist/connectors/config.d.ts.map +1 -1
- package/dist/connectors/config.js +4 -0
- package/dist/connectors/config.js.map +1 -1
- package/dist/connectors/data-store-mcp.d.ts +9 -0
- package/dist/connectors/data-store-mcp.d.ts.map +1 -1
- package/dist/connectors/data-store-mcp.js +25 -0
- package/dist/connectors/data-store-mcp.js.map +1 -1
- 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 +1 -0
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js +4 -0
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/skill-store-mcp.d.ts +17 -0
- package/dist/connectors/skill-store-mcp.d.ts.map +1 -0
- package/dist/connectors/skill-store-mcp.js +180 -0
- package/dist/connectors/skill-store-mcp.js.map +1 -0
- package/dist/connectors/types.d.ts +13 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js.map +1 -1
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +66 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +33 -41
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +9 -6
- package/dist/mcp-server.js.map +1 -1
- package/dist/mutation-gate.d.ts +67 -0
- package/dist/mutation-gate.d.ts.map +1 -0
- package/dist/mutation-gate.js +97 -0
- package/dist/mutation-gate.js.map +1 -0
- package/dist/parser.d.ts +13 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +32 -1
- package/dist/parser.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +73 -14
- package/dist/runtime.js.map +1 -1
- package/docs/adopter-playbook.md +76 -32
- package/docs/sqlite-skill-store.md +1 -1
- package/examples/README.md +7 -5
- package/examples/connectors/DataStoreTemplate/DataStoreTemplate.ts +22 -9
- package/examples/connectors/DataStoreTemplate/README.md +6 -6
- package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +7 -0
- package/examples/connectors/McpConnectorTemplate/README.md +3 -2
- package/examples/connectors/README.md +1 -1
- package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +26 -0
- package/examples/custom-bootstrap.example.ts +22 -4
- package/examples/onboarding-scaffold/README.md +3 -4
- package/examples/onboarding-scaffold/bootstrap.ts +25 -9
- package/examples/onboarding-scaffold/connectors.json +1 -1
- package/examples/onboarding-scaffold/file-data-store.ts +18 -7
- 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/data-store-roundtrip.skill.md +11 -0
- package/examples/skillscripts/doc-qa-with-citations.skill.md +3 -3
- package/examples/skillscripts/feedback-sentiment-scan.skill.md +8 -7
- package/examples/skillscripts/hello-world.skill.md +10 -0
- package/examples/skillscripts/{hello.skill.provenance.json → hello-world.skill.provenance.json} +2 -2
- package/examples/skillscripts/morning-brief.skill.md +1 -1
- package/examples/skillscripts/service-health-watch.skill.md +5 -4
- package/examples/skillscripts/skill-store-roundtrip.skill.md +11 -0
- package/package.json +1 -1
- package/scaffold/config.toml +4 -2
- package/scaffold/examples/hello-world.skill.md +10 -0
- package/examples/skillscripts/hello.skill.md +0 -9
- package/scaffold/examples/hello.skill.md +0 -10
- /package/examples/onboarding-scaffold/{memory.example.json → data.example.json} +0 -0
|
@@ -88,6 +88,13 @@ export class DataStoreTemplate implements DataStore {
|
|
|
88
88
|
* { capabilities_version: "1", manifest: { kind: "sqlite-fts",
|
|
89
89
|
* supported_modes: ["fts"], score_range: "unbounded",
|
|
90
90
|
* supported_filters: ["domain_tags"], supports_write: true } }
|
|
91
|
+
*
|
|
92
|
+
* **v0.14.1 — `supported_filters` is load-bearing.** The DataStoreMcpConnector
|
|
93
|
+
* bridge enforces this declaration: filter keys passed to `$ data_read` that
|
|
94
|
+
* aren't in `supported_filters` throw `UnsupportedFilterError` at the bridge
|
|
95
|
+
* BEFORE your `query()` runs. Adopters can opt out per-call with
|
|
96
|
+
* `permissive_filters: true`. Declare every filter your `query()` actually
|
|
97
|
+
* honors here; omissions become silent "this filter is rejected".
|
|
91
98
|
*/
|
|
92
99
|
async manifest(): Promise<ManifestInfo<"data_store">> {
|
|
93
100
|
// TODO — return a snapshot of your substrate's capabilities.
|
|
@@ -103,11 +110,17 @@ export class DataStoreTemplate implements DataStore {
|
|
|
103
110
|
* - `query`: string (search terms; substrate-specific interpretation)
|
|
104
111
|
* - `limit`: number (max results)
|
|
105
112
|
* - `mode`: "fts" | "semantic" | "rerank" | substrate-specific string
|
|
106
|
-
* - Plus
|
|
107
|
-
* `payload_type`, `pinned`, `agent_id`,
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
113
|
+
* - Plus filter fields you declared in `manifest().supported_filters`
|
|
114
|
+
* (`domain_tags`, `thread_status`, `payload_type`, `pinned`, `agent_id`,
|
|
115
|
+
* etc.). Per the curated-subset framing in `types.ts`, these top-level
|
|
116
|
+
* fields are first-class for substrates that have them.
|
|
117
|
+
*
|
|
118
|
+
* **v0.14.1 — what reaches `query()`.** The DataStoreMcpConnector bridge
|
|
119
|
+
* pre-filters the keys against your `manifest().supported_filters`
|
|
120
|
+
* declaration; unsupported keys throw `UnsupportedFilterError` upstream and
|
|
121
|
+
* never reach this method. So when `query()` runs, every non-base key in
|
|
122
|
+
* `filters` is one your manifest declared. Don't defensively code for
|
|
123
|
+
* "filter the caller didn't declare" — that's the bridge's job.
|
|
111
124
|
*
|
|
112
125
|
* `PortableData` core fields:
|
|
113
126
|
* - Always: `id`, `summary`, `created_at`
|
|
@@ -130,11 +143,11 @@ export class DataStoreTemplate implements DataStore {
|
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
/**
|
|
133
|
-
* Persist a new
|
|
146
|
+
* Persist a new data record. Return the substrate-assigned `id` +
|
|
134
147
|
* `created_at` (unix seconds).
|
|
135
148
|
*
|
|
136
149
|
* `DataWrite` shape:
|
|
137
|
-
* - `content`: string (required; the
|
|
150
|
+
* - `content`: string (required; the record body)
|
|
138
151
|
* - `tags`: string[] (optional; routed to substrate's tag mechanism)
|
|
139
152
|
* - `recipients`: string[] (optional advisory — substrates with alerting
|
|
140
153
|
* machinery use this to route notifications, e.g., AMP's mailbox model)
|
|
@@ -144,12 +157,12 @@ export class DataStoreTemplate implements DataStore {
|
|
|
144
157
|
*/
|
|
145
158
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
146
159
|
async write(_entry: DataWrite): Promise<DataWriteRecord> {
|
|
147
|
-
// TODO — persist the
|
|
160
|
+
// TODO — persist the record via your substrate.
|
|
148
161
|
// - Generate or accept an id (substrate-dependent)
|
|
149
162
|
// - Persist content + tags + metadata
|
|
150
163
|
// - Apply recipients hint if your substrate has alerting
|
|
151
164
|
// - Apply expires_at if your substrate has TTL
|
|
152
165
|
// - Return { id, created_at }
|
|
153
|
-
throw new Error("TODO: write() — persist
|
|
166
|
+
throw new Error("TODO: write() — persist record; return { id, created_at }.");
|
|
154
167
|
}
|
|
155
168
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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
7
|
- A data store backing (memory broker like AMP, hosted memory API, vector DB, etc.)
|
|
8
8
|
- A different SQL flavor (Postgres + pgvector, MySQL)
|
|
@@ -18,7 +18,7 @@ DataStore (choose which connector)
|
|
|
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
|
|
|
@@ -72,8 +72,8 @@ The DataStore contract is narrower than SkillStore. Three methods + `staticCapab
|
|
|
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
79
|
Per the curated-subset framing in `src/connectors/types.ts`, `PortableData` has a 4-tier field model:
|
|
@@ -113,7 +113,7 @@ Honor what your substrate supports; ignore the rest. Document the supported set
|
|
|
113
113
|
|
|
114
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,7 +121,7 @@ The `DataWrite` 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
|
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* - `DataStoreMcpConnector` — bridges a registered DataStore as
|
|
19
19
|
* `$ data_read mode=...` MCP dispatch. Auto-wired when DataStore substrate
|
|
20
20
|
* is configured.
|
|
21
|
+
* - `SkillStoreMcpConnector` (v0.15.0) — bridges a registered SkillStore as
|
|
22
|
+
* `$ skill_write` / `$ skill_read` MCP dispatch. Makes the Lisp-shape
|
|
23
|
+
* primitive (skills program skills) work from inside an executing skill.
|
|
24
|
+
* Auto-wired against `substrate.skill_store`. In-skill writes are forced
|
|
25
|
+
* to `# Status: Draft` at the bridge per the v0.15.0 trust boundary —
|
|
26
|
+
* see `src/connectors/skill-store-mcp.ts` header for the threat-model
|
|
27
|
+
* rationale.
|
|
21
28
|
*
|
|
22
29
|
* Fork from this template when none of those fit — e.g.:
|
|
23
30
|
* - Direct HTTP MCP (JSON-RPC over HTTP, no child process)
|
|
@@ -9,7 +9,8 @@ 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
|
-
| `DataStoreMcpConnector` | Bridges a registered `DataStore` as `$ data_read mode=...`. Auto-wired when `substrate.data_store` is set. |
|
|
12
|
+
| `DataStoreMcpConnector` | Bridges a registered `DataStore` as `$ data_read mode=...` + `$ data_write content=...`. Auto-wired when `substrate.data_store` is set. |
|
|
13
|
+
| `SkillStoreMcpConnector` (v0.15.0) | Bridges a registered `SkillStore` as in-skill `$ skill_write` + `$ skill_read` dispatch (Lisp-shape: skills program skills). Auto-wired against `substrate.skill_store`. In-skill writes are forced to `# Status: Draft` at the bridge per the v0.15.0 trust boundary. |
|
|
13
14
|
|
|
14
15
|
**Fork this template only when none of those fit** — e.g.:
|
|
15
16
|
|
|
@@ -139,7 +140,7 @@ The declarative path is the canonical adopter pattern for stdio-bridged remote M
|
|
|
139
140
|
| Cardinality | Many instances per deployment | One singleton per slot |
|
|
140
141
|
| Substrate config | Per-instance via top-level keys | `substrate` section short/object/custom |
|
|
141
142
|
| Class extensibility | `registerConnectorClass()` for adopter-custom classes | Programmatic bootstrap (or `substrate.skill_store: {type: "custom", ...}` once async-bootstrap lands) |
|
|
142
|
-
| Auto-wired bridges | `llm` + `
|
|
143
|
+
| Auto-wired bridges | `llm` + `data_read` + `data_write` + `skill_read` + `skill_write` (v0.15.0 — LocalModel + DataStore + SkillStore exposed via bridge connectors) | n/a (these ARE the substrates being bridged) |
|
|
143
144
|
|
|
144
145
|
McpConnector is fundamentally the "dispatch to external tools" surface — narrowest contract, broadest range of impls.
|
|
145
146
|
|
|
@@ -9,7 +9,7 @@ Worked examples + fork-me templates for adopter-written connectors. The bundled
|
|
|
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`, `DataStoreMcpConnector` (in `src/connectors/`) | — | **[McpConnectorTemplate/](./McpConnectorTemplate/)** |
|
|
12
|
+
| `McpConnector` | `RemoteMcpConnector`, `CallbackMcpConnector`, `LocalModelMcpConnector`, `DataStoreMcpConnector`, `SkillStoreMcpConnector` (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.
|
|
@@ -26,6 +26,32 @@
|
|
|
26
26
|
* you register via the registry, so once your fork passes the conformance
|
|
27
27
|
* suite the entire skillscript surface (skill_write / skill_list /
|
|
28
28
|
* execute_skill / etc.) reads + writes against your substrate.
|
|
29
|
+
*
|
|
30
|
+
* **v0.15.0 — `SkillStoreMcpConnector` bridge auto-wires against your impl.**
|
|
31
|
+
* The runtime's `defaultRegistry` wraps your registered SkillStore in a
|
|
32
|
+
* `SkillStoreMcpConnector` instance and registers it under the connector
|
|
33
|
+
* names `skill_read` + `skill_write`, so `$ skill_read` / `$ skill_write` from
|
|
34
|
+
* inside an executing skill dispatches through your `load()` / `store()`
|
|
35
|
+
* methods. This is the Lisp-shape primitive — skills can program skills —
|
|
36
|
+
* and works against any `SkillStore` impl that passes the conformance suite.
|
|
37
|
+
*
|
|
38
|
+
* Your impl doesn't need to do anything special to participate. Two things
|
|
39
|
+
* worth knowing:
|
|
40
|
+
* - **In-skill writes arrive Draft-stamped.** The bridge layer applies a
|
|
41
|
+
* `# Status: Draft` override to the body before calling your `store()`.
|
|
42
|
+
* This is the v0.15.0 trust boundary — autonomously-generated skill
|
|
43
|
+
* bodies (e.g., `~ qwen prompt="generate a skill"` → `$ skill_write`)
|
|
44
|
+
* shouldn't bypass human review by landing Approved + immediately
|
|
45
|
+
* executable. See `src/connectors/skill-store-mcp.ts` header for the
|
|
46
|
+
* threat-model rationale. Outside-MCP `skill_write` (cold-author agents
|
|
47
|
+
* authoring directly via the wire surface) keeps the existing "body
|
|
48
|
+
* declares status, you auto-stamp via store()" path — that's the path
|
|
49
|
+
* your `store()` honor-the-body-Status logic addresses.
|
|
50
|
+
* - **Substrate-level lint runs at `store()` entry.** If you mirror
|
|
51
|
+
* `FilesystemSkillStore` / `SqliteSkillStore`, you'll run tier-1 lint
|
|
52
|
+
* on the body before persisting + throw `LintFailureError` on rejection.
|
|
53
|
+
* The bridge propagates that error through `$ skill_write` so authors
|
|
54
|
+
* see lint-rejection-on-write, not silent persistence of malformed bodies.
|
|
29
55
|
*/
|
|
30
56
|
|
|
31
57
|
import type {
|
|
@@ -86,14 +86,32 @@ registry.registerSkillStore("primary", skillStore);
|
|
|
86
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.registerDataStore("primary", new
|
|
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 in-skill `$ llm` /
|
|
93
|
+
// `$ data_read` / `$ data_write` / `$ skill_read` / `$ skill_write`
|
|
94
|
+
// surfaces. Each bridge handles its multi-verb tool surface via toolName
|
|
95
|
+
// discrimination; register the same instance under each connector name so
|
|
96
|
+
// bare-form name-match resolution picks the right route.
|
|
97
|
+
// import { LocalModelMcpConnector, DataStoreMcpConnector, SkillStoreMcpConnector } from "skillscript-runtime";
|
|
94
98
|
// registry.registerMcpConnector("llm", new LocalModelMcpConnector(registry.getLocalModel("default")));
|
|
95
99
|
// const ms = registry.listDataStores().find((e) => e.name === "primary");
|
|
96
|
-
// if (ms !== undefined)
|
|
100
|
+
// if (ms !== undefined) {
|
|
101
|
+
// const dataBridge = new DataStoreMcpConnector(ms.instance);
|
|
102
|
+
// registry.registerMcpConnector("data_read", dataBridge);
|
|
103
|
+
// registry.registerMcpConnector("data_write", dataBridge);
|
|
104
|
+
// }
|
|
105
|
+
// // v0.15.0 — SkillStore-as-bridge. Makes `$ skill_write` / `$ skill_read`
|
|
106
|
+
// // dispatchable from inside an executing skill (Lisp-shape: skills program
|
|
107
|
+
// // skills). In-skill writes are Draft-forced at the bridge layer; outside-MCP
|
|
108
|
+
// // skill_write keeps the existing "body declares status" behavior.
|
|
109
|
+
// const ss = registry.listSkillStores().find((e) => e.name === "primary");
|
|
110
|
+
// if (ss !== undefined) {
|
|
111
|
+
// const skillBridge = new SkillStoreMcpConnector(ss.instance);
|
|
112
|
+
// registry.registerMcpConnector("skill_read", skillBridge);
|
|
113
|
+
// registry.registerMcpConnector("skill_write", skillBridge);
|
|
114
|
+
// }
|
|
97
115
|
|
|
98
116
|
// Wire any connectors.json instances.
|
|
99
117
|
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
|
|
|
@@ -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
|
|
@@ -49,7 +49,7 @@ node --loader ts-node/esm bootstrap.ts
|
|
|
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
|
|
|
@@ -77,4 +77,3 @@ The skill bodies don't need to change. `$ llm prompt=...` keeps working; `$ data
|
|
|
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** — `$ data_write` ships in v0.8.x bundled with the auth model. When that lands, extend `FileDataStore` with the corresponding `write()` method.
|
|
@@ -1,4 +1,4 @@
|
|
|
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
|
|
@@ -20,12 +20,13 @@ 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 + DataStoreMcpConnector
|
|
24
|
-
// already exported from skillscript-runtime/connectors.
|
|
25
|
-
// typed-contract impl as an MCP-dispatchable connector.
|
|
23
|
+
// Note: LocalModelMcpConnector + DataStoreMcpConnector + SkillStoreMcpConnector
|
|
24
|
+
// are bridge classes already exported from skillscript-runtime/connectors.
|
|
25
|
+
// They wrap any typed-contract impl as an MCP-dispatchable connector.
|
|
26
26
|
import {
|
|
27
27
|
LocalModelMcpConnector,
|
|
28
28
|
DataStoreMcpConnector,
|
|
29
|
+
SkillStoreMcpConnector,
|
|
29
30
|
} from "skillscript-runtime/connectors";
|
|
30
31
|
|
|
31
32
|
import { FileDataStore } from "./file-data-store.js";
|
|
@@ -43,9 +44,9 @@ const registry = new Registry();
|
|
|
43
44
|
const skillStore = new FilesystemSkillStore(config.skillsDir ?? `${HOME}/skills`);
|
|
44
45
|
registry.registerSkillStore("primary", skillStore);
|
|
45
46
|
|
|
46
|
-
// File-backed
|
|
47
|
+
// File-backed data store at $SKILLSCRIPT_HOME/data.json
|
|
47
48
|
const dataStore = new FileDataStore({
|
|
48
|
-
filePath: config.dataDbPath ?? `${HOME}/
|
|
49
|
+
filePath: config.dataDbPath ?? `${HOME}/data.json`,
|
|
49
50
|
});
|
|
50
51
|
registry.registerDataStore("primary", dataStore);
|
|
51
52
|
|
|
@@ -63,10 +64,25 @@ const agentConnector = new TmuxShellAgentConnector({
|
|
|
63
64
|
});
|
|
64
65
|
registry.registerAgentConnector("primary", agentConnector);
|
|
65
66
|
|
|
66
|
-
// Step 3: wire bridges so `$ llm` / `$ data_read`
|
|
67
|
-
//
|
|
67
|
+
// Step 3: wire bridges so canonical in-skill `$ llm` / `$ data_read` /
|
|
68
|
+
// `$ data_write` / `$ skill_read` / `$ skill_write` dispatch through the
|
|
69
|
+
// adopter substrates above (case 1 typed-contract wiring — portable). Each
|
|
70
|
+
// bridge handles its multi-verb surface via toolName discrimination;
|
|
71
|
+
// register the same instance under each connector name so bare-form
|
|
72
|
+
// name-match resolution picks the right route.
|
|
68
73
|
registry.registerMcpConnector("llm", new LocalModelMcpConnector(openai));
|
|
69
|
-
|
|
74
|
+
const dataBridge = new DataStoreMcpConnector(dataStore);
|
|
75
|
+
registry.registerMcpConnector("data_read", dataBridge);
|
|
76
|
+
registry.registerMcpConnector("data_write", dataBridge);
|
|
77
|
+
// v0.15.0 — SkillStore-as-bridge. Makes `$ skill_write` / `$ skill_read`
|
|
78
|
+
// dispatchable from inside an executing skill (Lisp-shape: skills program
|
|
79
|
+
// skills). In-skill writes are forced to `# Status: Draft` at the bridge
|
|
80
|
+
// layer per the v0.15.0 trust boundary — see `src/connectors/skill-store-mcp.ts`
|
|
81
|
+
// header for the threat-model rationale. Outside-MCP `skill_write` (via the
|
|
82
|
+
// wire surface) keeps its existing "body declares status" behavior.
|
|
83
|
+
const skillBridge = new SkillStoreMcpConnector(skillStore);
|
|
84
|
+
registry.registerMcpConnector("skill_read", skillBridge);
|
|
85
|
+
registry.registerMcpConnector("skill_write", skillBridge);
|
|
70
86
|
|
|
71
87
|
// Step 4: wire connectors.json instances (adopter-defined MCP servers).
|
|
72
88
|
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",
|
|
@@ -22,11 +22,11 @@ import type {
|
|
|
22
22
|
} from "skillscript-runtime/connectors";
|
|
23
23
|
|
|
24
24
|
export interface FileDataStoreConfig {
|
|
25
|
-
/** Absolute path to the JSON file holding the
|
|
25
|
+
/** Absolute path to the JSON file holding the data records. */
|
|
26
26
|
filePath: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
interface
|
|
29
|
+
interface FileDataRecord extends PortableData {
|
|
30
30
|
/** Optional substrate-specific fields go in `metadata`; everything top-level matches PortableData. */
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -77,7 +77,7 @@ export class FileDataStore implements DataStore {
|
|
|
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 FileDataStore implements DataStore {
|
|
|
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 };
|
|
@@ -106,19 +106,30 @@ export class FileDataStore implements DataStore {
|
|
|
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(`FileDataStore: '${this.config.filePath}' top-level must be an array of
|
|
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
134
|
throw new Error(`FileDataStore: failed to read '${this.config.filePath}': ${(err as Error).message}`);
|
|
124
135
|
}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
# Skill: classify-support-ticket
|
|
2
|
-
# Status: Approved v1:
|
|
2
|
+
# Status: Approved v1:670a94cf
|
|
3
|
+
# Autonomous: true
|
|
3
4
|
# Description: Read an incoming support ticket and route it: severity-1 tickets get paged to ops-channel, billing tickets get tagged for finance review, everything else gets a draft reply queued for human review
|
|
4
5
|
# Vars: TICKET_TEXT, TICKET_ID
|
|
5
6
|
# Output: agent: ops-oncall
|
|
6
7
|
|
|
7
8
|
classify:
|
|
8
|
-
$ llm prompt="Categorize this support ticket. Reply with EXACTLY one of: 'sev-1', 'billing', 'general'. No other text.\n\nTicket: ${TICKET_TEXT}"
|
|
9
|
+
$ llm prompt="Categorize this support ticket. Reply with EXACTLY one of: 'sev-1', 'billing', 'general'. No other text.\n\nTicket: ${TICKET_TEXT}" maxTokens=10 -> CATEGORY
|
|
9
10
|
|
|
10
11
|
severity_check:
|
|
11
12
|
needs: classify
|
|
12
|
-
$ llm prompt="Does this support ticket describe a service outage, data loss, security incident, or other production-severity-1 issue? Reply ONLY 'yes' or 'no'.\n\nTicket: ${TICKET_TEXT}"
|
|
13
|
+
$ llm prompt="Does this support ticket describe a service outage, data loss, security incident, or other production-severity-1 issue? Reply ONLY 'yes' or 'no'.\n\nTicket: ${TICKET_TEXT}" maxTokens=10 -> IS_SEV1
|
|
13
14
|
|
|
14
15
|
route:
|
|
15
16
|
needs: severity_check
|
|
16
17
|
if ${CATEGORY|trim} == "sev-1":
|
|
17
18
|
emit(text="PAGE: sev-1 ticket ${TICKET_ID} - ${TICKET_TEXT}")
|
|
18
|
-
$
|
|
19
|
+
$ data_write content="sev-1 ticket ${TICKET_ID}: ${TICKET_TEXT}" tags=["support","sev-1","page"]
|
|
19
20
|
elif ${IS_SEV1|trim} == "yes":
|
|
20
21
|
emit(text="PAGE: classifier said ${CATEGORY|trim} but severity-check flagged this as sev-1: ${TICKET_ID}")
|
|
21
|
-
$
|
|
22
|
+
$ data_write content="sev-1 escalation ${TICKET_ID}: category=${CATEGORY|trim} but severity-check=yes" tags=["support","sev-1","disagreement"]
|
|
22
23
|
elif ${CATEGORY|trim} == "billing":
|
|
23
24
|
$set TAG_FOR = "finance"
|
|
24
25
|
emit(text="Tagged for ${TAG_FOR} review: ${TICKET_ID}")
|
|
25
|
-
$
|
|
26
|
+
$ data_write content="billing ticket ${TICKET_ID}: ${TICKET_TEXT}" tags=["support","billing"]
|
|
26
27
|
else:
|
|
27
|
-
$ llm prompt="Draft a polite acknowledgment reply for this support ticket. Two short sentences. No greeting, no sign-off.\n\n${TICKET_TEXT}"
|
|
28
|
-
$
|
|
28
|
+
$ llm prompt="Draft a polite acknowledgment reply for this support ticket. Two short sentences. No greeting, no sign-off.\n\n${TICKET_TEXT}" maxTokens=150 -> DRAFT
|
|
29
|
+
$ data_write content="support draft ${TICKET_ID}: ${DRAFT|trim}" tags=["support","draft"]
|
|
29
30
|
|
|
30
31
|
default: route
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: cut-release-tag
|
|
2
|
-
# Status: Approved v1:
|
|
2
|
+
# Status: Approved v1:d3c2a91b
|
|
3
3
|
# Description: Run when the user says "ship a release" or "cut a tag" on the current branch; collects commit-list since last tag, asks the user to confirm the version bump, applies the tag, and pushes it
|
|
4
4
|
# Vars: BUMP_KIND=patch
|
|
5
5
|
# Output: text
|
|
@@ -12,13 +12,13 @@ else:
|
|
|
12
12
|
|
|
13
13
|
commits_since:
|
|
14
14
|
needs: last_tag
|
|
15
|
-
shell(command="git log --oneline ${PREV_TAG}..HEAD") -> COMMITS
|
|
15
|
+
shell(command="git log --oneline \"${PREV_TAG}\"..HEAD") -> COMMITS
|
|
16
16
|
else:
|
|
17
17
|
$set COMMITS = "(unable to read commit log; proceeding without preview)"
|
|
18
18
|
|
|
19
19
|
propose:
|
|
20
20
|
needs: commits_since
|
|
21
|
-
$ llm prompt="Propose the next semver tag given prev tag '${PREV_TAG}', bump kind '${BUMP_KIND}', and commits below. Return ONLY the new tag string (e.g. v1.4.3). No commentary.\n\nCommits:\n${COMMITS}"
|
|
21
|
+
$ llm prompt="Propose the next semver tag given prev tag '${PREV_TAG}', bump kind '${BUMP_KIND}', and commits below. Return ONLY the new tag string (e.g. v1.4.3). No commentary.\n\nCommits:\n${COMMITS}" maxTokens=30 -> PROPOSED_TAG
|
|
22
22
|
|
|
23
23
|
confirm:
|
|
24
24
|
needs: propose
|
|
@@ -30,11 +30,11 @@ confirm:
|
|
|
30
30
|
|
|
31
31
|
apply:
|
|
32
32
|
needs: confirm
|
|
33
|
-
shell(command="git tag ${PROPOSED_TAG|trim}") -> TAG_OUT
|
|
34
|
-
shell(command="git push origin ${PROPOSED_TAG|trim}") -> PUSH_OUT
|
|
33
|
+
shell(command="git tag \"${PROPOSED_TAG|trim}\"") -> TAG_OUT
|
|
34
|
+
shell(command="git push origin \"${PROPOSED_TAG|trim}\"") -> PUSH_OUT
|
|
35
35
|
emit(text="Tagged and pushed ${PROPOSED_TAG|trim}")
|
|
36
36
|
else:
|
|
37
37
|
emit(text="Tag/push failed; rolling back local tag")
|
|
38
|
-
shell(command="git tag -d ${PROPOSED_TAG|trim}") -> ROLLBACK
|
|
38
|
+
shell(command="git tag -d \"${PROPOSED_TAG|trim}\"") -> ROLLBACK
|
|
39
39
|
|
|
40
40
|
default: apply
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Skill: data-store-roundtrip
|
|
2
|
+
# Status: Approved v1:83897803
|
|
3
|
+
# Autonomous: true
|
|
4
|
+
# Description: Round-trips the DataStore — writes a record, reads it back via full-text search. If this skill executes and emits a successful round-trip, your DataStore substrate is wired correctly.
|
|
5
|
+
|
|
6
|
+
run:
|
|
7
|
+
$ data_write content="adopter dogfood probe record" tags=["phase1","probe"] -> W
|
|
8
|
+
$ data_read mode="fts" query="adopter dogfood probe" limit=5 -> R
|
|
9
|
+
emit(text="wrote record '${W.id}'; read back ${R.items|length} items")
|
|
10
|
+
|
|
11
|
+
default: run
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Skill: doc-qa-with-citations
|
|
2
|
-
# Status: Approved v1:
|
|
3
|
-
# Description: When the user asks a question that requires retrieval over the doc set, answer with inline citations to
|
|
2
|
+
# Status: Approved v1:6e5dc167
|
|
3
|
+
# Description: When the user asks a question that requires retrieval over the doc set, answer with inline citations to record IDs
|
|
4
4
|
# Vars: QUESTION, K=6
|
|
5
5
|
# Output: text
|
|
6
6
|
|
|
7
7
|
answer:
|
|
8
8
|
$ data_read mode=rerank query="${QUESTION}" limit=${K} -> HITS (fallback: [])
|
|
9
|
-
$ llm prompt="Answer the question using ONLY the supplied passages. Cite each claim inline as [id:<
|
|
9
|
+
$ llm prompt="Answer the question using ONLY the supplied passages. Cite each claim inline as [id:<record-id>]. Question: ${QUESTION}. Passages: ${HITS|json}" maxTokens=900 -> RESPONSE
|
|
10
10
|
emit(text="${RESPONSE}")
|
|
11
11
|
|
|
12
12
|
default: answer
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Skill: feedback-sentiment-scan
|
|
2
|
-
# Status: Approved v1:
|
|
2
|
+
# Status: Approved v1:4484f7e0
|
|
3
|
+
# Autonomous: true
|
|
3
4
|
# Description: Each night, scan the previous 24h of customer feedback records, classify sentiment via local model, surface entries where sentiment is "frustrated" or "blocking" so the team sees them at start-of-day; skip entries already seen on prior nights
|
|
4
5
|
# Triggers: cron: 0 3 * * *
|
|
5
6
|
# Vars: SCAN_LIMIT=50
|
|
6
7
|
# Output: agent: support-lead
|
|
7
8
|
|
|
8
9
|
fetch_new:
|
|
9
|
-
$ data_read mode=fts query="customer feedback" limit=${SCAN_LIMIT}
|
|
10
|
+
$ data_read mode=fts query="customer feedback" limit=${SCAN_LIMIT} -> FEEDBACK
|
|
10
11
|
|
|
11
12
|
fetch_seen:
|
|
12
13
|
$ data_read mode=fts query="sentiment-scan seen marker" limit=200 domain_tags=["sentiment-scan-seen"] -> SEEN_MARKERS
|
|
@@ -14,15 +15,15 @@ fetch_seen:
|
|
|
14
15
|
classify_and_emit:
|
|
15
16
|
needs: fetch_new, fetch_seen
|
|
16
17
|
emit(text="Sentiment scan results for ${NOW}:")
|
|
17
|
-
foreach F in ${FEEDBACK}:
|
|
18
|
-
if ${F.id|trim} in ${SEEN_MARKERS}:
|
|
18
|
+
foreach F in ${FEEDBACK.items}:
|
|
19
|
+
if ${F.id|trim} in ${SEEN_MARKERS.items}:
|
|
19
20
|
emit(text="- skipped (already classified): ${F.id|trim}")
|
|
20
|
-
elif ${F.id|trim} not in ${SEEN_MARKERS}:
|
|
21
|
-
$ llm prompt="Classify the sentiment of this customer feedback. Respond with ONE word: 'frustrated', 'blocking', 'satisfied', 'neutral'. No explanation.\n\nFeedback: ${F.summary}\nDetail: ${F.detail}"
|
|
21
|
+
elif ${F.id|trim} not in ${SEEN_MARKERS.items}:
|
|
22
|
+
$ llm prompt="Classify the sentiment of this customer feedback. Respond with ONE word: 'frustrated', 'blocking', 'satisfied', 'neutral'. No explanation.\n\nFeedback: ${F.summary}\nDetail: ${F.detail}" maxTokens=10 -> VERDICT
|
|
22
23
|
if ${VERDICT|trim} == "frustrated":
|
|
23
24
|
emit(text="- FRUSTRATED [${F.id|trim}] ${F.summary}")
|
|
24
25
|
elif ${VERDICT|trim} == "blocking":
|
|
25
26
|
emit(text="- BLOCKING [${F.id|trim}] ${F.summary}")
|
|
26
|
-
$
|
|
27
|
+
$ data_write content="sentiment-scan seen ${F.id|trim} verdict=${VERDICT|trim} on ${EVENT.fired_at_unix}" tags=["sentiment-scan-seen"] expires_at=${EVENT.fired_at_plus_7d_unix} -> ACK
|
|
27
28
|
|
|
28
29
|
default: classify_and_emit
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Skill: hello-world
|
|
2
|
+
# Status: Approved v1:5ce6d5bf
|
|
3
|
+
# Description: The canonical first-run example. No substrate dependencies — pure emit. If this fails to execute, your install or runtime is broken; investigate before troubleshooting deeper layers. Demonstrates `# Vars:` declared inputs with `--input` override.
|
|
4
|
+
# Vars: WHO=world
|
|
5
|
+
|
|
6
|
+
greet:
|
|
7
|
+
emit(text="Hello, ${WHO}!")
|
|
8
|
+
emit(text="Your install is healthy. Try skill-store-roundtrip + data-store-roundtrip next to verify substrate wiring.")
|
|
9
|
+
|
|
10
|
+
default: greet
|
package/examples/skillscripts/{hello.skill.provenance.json → hello-world.skill.provenance.json}
RENAMED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"provenance_version": "1.0",
|
|
3
3
|
"language_version": "1.0",
|
|
4
4
|
"compiler_version": "0.1.0-dev",
|
|
5
|
-
"compiled_at": "2026-
|
|
5
|
+
"compiled_at": "2026-06-01T14:54:14.858Z",
|
|
6
6
|
"source_skill": {
|
|
7
|
-
"name": "hello"
|
|
7
|
+
"name": "hello-world"
|
|
8
8
|
},
|
|
9
9
|
"data_skills_inlined": []
|
|
10
10
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: morning-brief
|
|
2
|
-
# Status: Approved v1:
|
|
2
|
+
# Status: Approved v1:091ae2ae
|
|
3
3
|
# Description: Compose a daily morning brief from calendar, mailbox, and overnight data writes when the cron trigger fires at 7am. Delivers via the agent: lifecycle hook to the receiving agent, who decides whether to surface to Slack / Discord / etc.
|
|
4
4
|
# Vars: AGENT, BRIEF_HORIZON_HOURS=24
|
|
5
5
|
# Triggers: cron: 0 7 * * *
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Skill: service-health-watch
|
|
2
|
-
# Status: Approved v1:
|
|
3
|
-
#
|
|
2
|
+
# Status: Approved v1:628c8c69
|
|
3
|
+
# Autonomous: true
|
|
4
|
+
# Description: Every 5 minutes check named service endpoints — if latency or status degrades, write a signal record and alert
|
|
4
5
|
# Vars: SERVICES=[auth-api, ledger-api, search-api], LATENCY_BUDGET_MS=400
|
|
5
6
|
# Triggers: cron: */5 * * * *
|
|
6
7
|
# Output: none
|
|
@@ -10,8 +11,8 @@ probe:
|
|
|
10
11
|
shell(command="curl -s -o /dev/null -w \"%{http_code} %{time_total}\" https://status.internal/${SVC|url}") -> RAW
|
|
11
12
|
$ llm prompt="From the line '${RAW|trim}' (http_code time_seconds), and budget ${LATENCY_BUDGET_MS} ms, answer ok or degraded only." -> STATUS
|
|
12
13
|
if ${STATUS|trim} == "degraded":
|
|
13
|
-
$
|
|
14
|
-
emit(text="${SVC} degraded — wrote signal")
|
|
14
|
+
$ data_write content="service degradation: ${SVC} at ${NOW}: ${RAW|trim}" tags=["ops","service-health","degraded:${SVC}"] expires_at=${EVENT.fired_at_plus_1d_unix} -> ACK
|
|
15
|
+
emit(text="${SVC} degraded — wrote signal ${ACK.id}")
|
|
15
16
|
else:
|
|
16
17
|
emit(text="${SVC} ok")
|
|
17
18
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Skill: skill-store-roundtrip
|
|
2
|
+
# Status: Approved v1:a3d46cf0
|
|
3
|
+
# Autonomous: true
|
|
4
|
+
# Description: Round-trips the SkillStore — writes a child skill, reads it back. Demonstrates the Lisp-shape primitive (skills can write skills). NOTE: under v0.15.0, in-skill `$ skill_write` lands the child as `# Status: Draft` regardless of what the body declares — to run the generated child, an authorized agent (human via dashboard, or MCP-direct) reviews + promotes via the outside-MCP `skill_status` tool. The Draft-default gate keeps autonomously-written skills out of the immediate execution loop.
|
|
5
|
+
|
|
6
|
+
run:
|
|
7
|
+
$ skill_write name="hello-child" source="# Skill: hello-child\n# Status: Approved\nrun:\n emit(text=\"hello from a programmatically-authored skill\")\ndefault: run\n" -> W
|
|
8
|
+
$ skill_read name="hello-child" -> R
|
|
9
|
+
emit(text="wrote skill '${W.name}' as Status: ${W.status}; read back ${R.source|length} bytes")
|
|
10
|
+
|
|
11
|
+
default: run
|