skillscript-runtime 0.13.8 → 0.14.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 +11 -11
- package/dist/bootstrap.d.ts +13 -13
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +31 -31
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/compile.js +2 -2
- package/dist/compile.js.map +1 -1
- package/dist/connectors/config.d.ts +4 -4
- package/dist/connectors/config.d.ts.map +1 -1
- package/dist/connectors/config.js +7 -7
- package/dist/connectors/config.js.map +1 -1
- package/dist/connectors/{memory-store-mcp.d.ts → data-store-mcp.d.ts} +8 -8
- package/dist/connectors/data-store-mcp.d.ts.map +1 -0
- package/dist/connectors/{memory-store-mcp.js → data-store-mcp.js} +31 -31
- package/dist/connectors/data-store-mcp.js.map +1 -0
- package/dist/connectors/{memory-store.d.ts → data-store.d.ts} +12 -12
- package/dist/connectors/data-store.d.ts.map +1 -0
- package/dist/connectors/{memory-store.js → data-store.js} +7 -7
- package/dist/connectors/data-store.js.map +1 -0
- package/dist/connectors/index.d.ts +5 -5
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js +7 -7
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/local-model-mcp.js +1 -1
- package/dist/connectors/local-model-mcp.js.map +1 -1
- package/dist/connectors/registry.d.ts +10 -10
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +11 -11
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +1 -1
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/connectors/types.d.ts +23 -23
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js +2 -2
- package/dist/connectors/types.js.map +1 -1
- package/dist/dashboard/spa/app.js +2 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +4 -4
- package/dist/errors.js.map +1 -1
- package/dist/help-content.js +27 -27
- package/dist/help-content.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/lint.js +18 -18
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.js +10 -10
- package/dist/mcp-server.js.map +1 -1
- package/dist/runtime-config.d.ts +2 -2
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +1 -1
- package/dist/runtime-config.js.map +1 -1
- package/dist/runtime.js +6 -6
- package/dist/runtime.js.map +1 -1
- package/dist/skill-manager.js +1 -1
- package/dist/skill-manager.js.map +1 -1
- package/dist/testing/conformance.d.ts +8 -8
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +8 -8
- package/dist/testing/conformance.js.map +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/trace.d.ts +3 -3
- package/docs/adopter-playbook.md +18 -18
- package/docs/configuration.md +8 -8
- package/docs/connector-contract-reference.md +9 -9
- package/docs/language-reference.md +38 -63
- package/docs/sqlite-skill-store.md +1 -1
- package/examples/README.md +5 -5
- package/examples/connectors/{MemoryStoreTemplate/MemoryStoreTemplate.ts → DataStoreTemplate/DataStoreTemplate.ts} +34 -34
- package/examples/connectors/{MemoryStoreTemplate → DataStoreTemplate}/README.md +36 -36
- package/examples/connectors/LocalModelTemplate/README.md +1 -1
- package/examples/connectors/McpConnectorTemplate/McpConnectorTemplate.ts +2 -2
- package/examples/connectors/McpConnectorTemplate/README.md +5 -5
- package/examples/connectors/README.md +3 -3
- package/examples/connectors/SkillStoreTemplate/README.md +1 -1
- package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +1 -1
- package/examples/custom-bootstrap.example.ts +8 -8
- package/examples/onboarding-scaffold/README.md +7 -7
- package/examples/onboarding-scaffold/bootstrap.ts +10 -10
- package/examples/onboarding-scaffold/{file-memory-store.ts → file-data-store.ts} +18 -18
- package/examples/skillscripts/classify-support-ticket.skill.md +4 -4
- package/examples/skillscripts/doc-qa-with-citations.skill.md +1 -1
- package/examples/skillscripts/feedback-sentiment-scan.skill.md +3 -3
- package/examples/skillscripts/hello.skill.provenance.json +1 -1
- package/examples/skillscripts/morning-brief.skill.md +3 -3
- package/examples/skillscripts/service-health-watch.skill.md +1 -1
- package/package.json +1 -1
- package/scaffold/config.toml +2 -2
- package/scaffold/connectors.json +3 -3
- package/dist/connectors/memory-store-mcp.d.ts.map +0 -1
- package/dist/connectors/memory-store-mcp.js.map +0 -1
- package/dist/connectors/memory-store.d.ts.map +0 -1
- package/dist/connectors/memory-store.js.map +0 -1
package/docs/adopter-playbook.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Adopter playbook
|
|
2
2
|
|
|
3
|
-
How to wire skillscript-runtime into your deployment. Written for Joe-Programmer: you have your own substrate stack (
|
|
3
|
+
How to wire skillscript-runtime into your deployment. Written for Joe-Programmer: you have your own substrate stack (data store, agent harness, LLM endpoint, filesystem), and you want skillscript to slot in rather than dictate.
|
|
4
4
|
|
|
5
5
|
This playbook covers the load-bearing decisions, the two wiring patterns, and the conventions that keep your local modifications upstream-merge-friendly.
|
|
6
6
|
|
|
@@ -9,11 +9,11 @@ This playbook covers the load-bearing decisions, the two wiring patterns, and th
|
|
|
9
9
|
Skillscript-runtime is substrate-neutral and assumes you have (or will choose):
|
|
10
10
|
|
|
11
11
|
1. **A filesystem** — for skill source files (`.skill.md`), trace records, possibly a memory database. Sandbox via container, chroot, or limited-privilege process — operator's call.
|
|
12
|
-
2. **A
|
|
12
|
+
2. **A data store** — for knowledge retrieval and data writes. Could be SQLite-FTS (bundled), a vector database, an in-house store, an Obsidian-style notes system, a memory broker — whatever you already have.
|
|
13
13
|
3. **An LLM endpoint** — Ollama running locally (bundled), a hosted API like OpenAI / Anthropic / Azure, or your own inference server.
|
|
14
14
|
4. **An agent harness** — where skill output is delivered. Could be tmux sessions, a webhook receiver, an in-house agent runtime, or no harness at all (skills run for their text output only).
|
|
15
15
|
|
|
16
|
-
Each of these maps to a typed connector contract: `SkillStore`, `
|
|
16
|
+
Each of these maps to a typed connector contract: `SkillStore`, `DataStore`, `LocalModel`, `AgentConnector`. Plus `McpConnector` for any external tool you want to invoke from a skill body.
|
|
17
17
|
|
|
18
18
|
## Case 1 vs Case 2 — the load-bearing wiring decision
|
|
19
19
|
|
|
@@ -21,21 +21,21 @@ This is the most important architectural choice you'll make.
|
|
|
21
21
|
|
|
22
22
|
### Case 1 — typed-contract wiring (substrate-portable)
|
|
23
23
|
|
|
24
|
-
You implement the typed connector contracts (`
|
|
24
|
+
You implement the typed connector contracts (`DataStore`, `LocalModel`, etc.) against your substrate. The bridge classes (`DataStoreMcpConnector`, `LocalModelMcpConnector`) surface them as canonical `$ data_read` / `$ llm` dispatch.
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
class
|
|
28
|
-
async query(filters: QueryFilters): Promise<
|
|
27
|
+
class MyDataStore implements DataStore {
|
|
28
|
+
async query(filters: QueryFilters): Promise<PortableData[]> { /* ... */ }
|
|
29
29
|
async manifest(): Promise<ManifestInfo> { /* ... */ }
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
registry.
|
|
33
|
-
registry.registerMcpConnector("
|
|
32
|
+
registry.registerDataStore("primary", new MyDataStore());
|
|
33
|
+
registry.registerMcpConnector("data_read", new DataStoreMcpConnector(new MyDataStore()));
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
**In skills:**
|
|
37
37
|
```
|
|
38
|
-
$
|
|
38
|
+
$ data_read mode=fts query="customer feedback" limit=10 -> CONTEXT
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
This same skill body runs unchanged against your substrate, against SQLite-FTS (bundled), against Pinecone, against any substrate that conforms to the typed contract. **Skills are portable.**
|
|
@@ -100,7 +100,7 @@ Create `skillscript.config.json` in your `$SKILLSCRIPT_HOME`:
|
|
|
100
100
|
{
|
|
101
101
|
"skillsDir": "${SKILLSCRIPT_HOME}/skills",
|
|
102
102
|
"traceDir": "${SKILLSCRIPT_HOME}/traces",
|
|
103
|
-
"
|
|
103
|
+
"dataDbPath": "${SKILLSCRIPT_HOME}/memory.json",
|
|
104
104
|
"dashboard": { "port": 7878 }
|
|
105
105
|
}
|
|
106
106
|
```
|
|
@@ -154,7 +154,7 @@ src/connectors/local/my-memory-adapter.ts ← adopter-owned
|
|
|
154
154
|
src/connectors/local/my-llm-adapter.ts ← adopter-owned
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
Upstream changes to `src/connectors/
|
|
157
|
+
Upstream changes to `src/connectors/data-store.ts` won't conflict with your `local/` files.
|
|
158
158
|
|
|
159
159
|
### 2. Use the public registration API; don't edit the closed-set Map
|
|
160
160
|
|
|
@@ -182,17 +182,17 @@ See `examples/custom-bootstrap.example.ts` for a worked walkthrough.
|
|
|
182
182
|
| Substrate | Shipped contract | Shipped impls | Shipped bridge |
|
|
183
183
|
|---|---|---|---|
|
|
184
184
|
| SkillStore | ✓ `load`/`query`/`store`/`update_status`/`delete`/`versions`/`metadata`/`staticCapabilities` | `FilesystemSkillStore`, `SqliteSkillStore` | n/a |
|
|
185
|
-
|
|
|
185
|
+
| DataStore | ✓ `query`/`write` | `SqliteDataStore` | ✓ `DataStoreMcpConnector` |
|
|
186
186
|
| LocalModel | ✓ `run` | `OllamaLocalModel` | ✓ `LocalModelMcpConnector` |
|
|
187
187
|
| McpConnector | ✓ `call` | `RemoteMcpConnector`, `CallbackMcpConnector` | n/a |
|
|
188
188
|
| AgentConnector | ✓ `list_agents`/`deliver`/`wake`/`health_check`/`request_response`; optional `agent_status` | `NoOpAgentConnector` (default), `HttpWebhookAgentConnector` | n/a |
|
|
189
189
|
|
|
190
190
|
**Notable gaps the playbook should be honest about:**
|
|
191
191
|
|
|
192
|
-
- **`
|
|
193
|
-
- **SkillStore and
|
|
194
|
-
- **Durability is implementer's responsibility.** The typed SkillStore and
|
|
195
|
-
- **Filter scope is advisory today; v0.14.0 will make it enforceable.** `query(filters)` accepts arbitrary fields; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For scope/visibility/security-relevant filters (any future `vault` filter, tenant-id, access-control) the silent-drop is a leak waiting to happen. Until v0.14.0's `strict_filters: true`, enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/
|
|
192
|
+
- **`SqliteDataStore` is a deliberately minimal reference impl.** It satisfies the contract (`query` / `write` / `get` / `staticCapabilities` / `manifest`) and exposes `supports_writes` + `supports_tag_filter` as true. It does NOT support semantic retrieval, pinning, decay scoring, or thread-status filtering (`supports_semantic` / `supports_pinning` / `supports_decay_model` / `supports_thread_status_filter` are all false). The bundled impl exists so the runtime works out-of-box for FTS-style tag/text retrieval. Deployments that need richer query semantics fork `examples/connectors/DataStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). Check `manifest()` at runtime to confirm what your wired DataStore actually supports.
|
|
193
|
+
- **SkillStore and DataStore have different lifecycle models — by design.** SkillStore is mutable / versioned / named CRUD (Draft→Approved→Disabled→Delete with audit trail; specific named artifacts). DataStore is an append-only search log (write → query/get; no per-record lifecycle beyond TTL/decay if substrate has one). Backing both onto one substrate (per [[project_memorystore_holds_skills]] — skill payloads ride as `payload_type: "skill"` memories) means serving both lifecycle models at once. The contract doesn't state the asymmetry as a design stance, but it's load-bearing: substrates that conflate "memory expiry" with "skill expiry" silently break authored code. Phase 3 cold-adopter dogfood surfaced this — AMP's 7-day TTL on `knowledge_type: common` memories would have decayed skills if AmpSkillStore had defaulted to `common`. Adopter mitigation: pick a `knowledge_type` your substrate treats as durable (e.g., `hard_won` in AMP), or wrap the SkillStore with a re-pin sweep.
|
|
194
|
+
- **Durability is implementer's responsibility.** The typed SkillStore and DataStore contracts assume durable storage. Neither interface declares "writes live forever" — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly. **If your substrate has these behaviors, build adopter-side guards** (pin-rules, retention policies, periodic re-pin sweeps) OR pick a substrate posture that satisfies "durable forever." The contract doesn't enforce — silent staleness is the failure mode. See `docs/connector-contract-reference.md` § "Storage-layer conventions" for the full discussion.
|
|
195
|
+
- **Filter scope is advisory today; v0.14.0 will make it enforceable.** `query(filters)` accepts arbitrary fields; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For scope/visibility/security-relevant filters (any future `vault` filter, tenant-id, access-control) the silent-drop is a leak waiting to happen. Until v0.14.0's `strict_filters: true`, enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/DataStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns. Banked meta-pattern ([[feedback_defaults_over_knobs_security]]): defaults that protect by default > knobs that adopters opt into for security-relevant surfaces; aware adopters opt out, naive adopters get protection.
|
|
196
196
|
- **4 of 6 trigger sources parse but don't fire.** `cron` and `session: start` work; `event`, `agent-event`, `file-watch`, `sensor` are parser-only stubs awaiting the event-bus surface.
|
|
197
197
|
- **Output kinds are intentionally substrate-neutral.** `# Output:` accepts `text` / `agent: <name>` / `template: <name>` / `file: <path>` / `none`. Substrate-specific values (`slack:`, `card:`, etc.) are deliberately out of scope — adopters wanting Slack / WhatsApp / Discord / etc. delivery use either `$ slack.post ...` MCP dispatch inside the skill body OR deliver via `agent: <name>` and let the receiving agent decide.
|
|
198
198
|
- **Authorization is hash-token approval.** Skills must carry `# Status: Approved vN:<token>` where the token re-computes from the body minus its `# Status:` line. Bundled `v1:` is CRC32 — discipline-barrier strength, suited to single-operator deployments. Adversarial threat models swap a stronger function:
|
|
@@ -212,10 +212,10 @@ See `examples/custom-bootstrap.example.ts` for a worked walkthrough.
|
|
|
212
212
|
|
|
213
213
|
## Skill discovery + cross-agent composition
|
|
214
214
|
|
|
215
|
-
Under Case-1 wiring against a memory substrate that holds skill payloads (e.g., AMP's payload_type model where compiled `.skill.md` artifacts live alongside thread / prose / document entries), skill discovery uses the canonical `$
|
|
215
|
+
Under Case-1 wiring against a memory substrate that holds skill payloads (e.g., AMP's payload_type model where compiled `.skill.md` artifacts live alongside thread / prose / document entries), skill discovery uses the canonical `$ data_read` surface:
|
|
216
216
|
|
|
217
217
|
```
|
|
218
|
-
$
|
|
218
|
+
$ data_read mode=fts query="incident triage" limit=5 -> SKILLS
|
|
219
219
|
foreach S in ${SKILLS.items}:
|
|
220
220
|
execute_skill(skill_name="${S.name}", ...) -> RESULT
|
|
221
221
|
```
|
package/docs/configuration.md
CHANGED
|
@@ -4,7 +4,7 @@ How to configure a skillscript-runtime deployment.
|
|
|
4
4
|
|
|
5
5
|
The single config file is **`~/.skillscript/connectors.json`** (or any path passed via `--connectors`). It has two top-level concerns:
|
|
6
6
|
|
|
7
|
-
1. **`substrate`** — which `SkillStore`, `
|
|
7
|
+
1. **`substrate`** — which `SkillStore`, `DataStore`, and `LocalModel` the runtime hosts (MCP server + web dashboard) use.
|
|
8
8
|
2. **Named MCP connector instances** — `youtrack`, `github`, etc. — invoked via `$ <name>` in skill source.
|
|
9
9
|
|
|
10
10
|
The runtime loads `connectors.json` at startup. Missing file → graceful empty config (substrate defaults to filesystem skills + conditional sqlite memories; no MCP connectors). Malformed JSON or unknown fields → structured errors surfaced at bootstrap.
|
|
@@ -22,7 +22,7 @@ Every default path the runtime computes is rooted under `SKILLSCRIPT_HOME`:
|
|
|
22
22
|
| Triggers file | `$SKILLSCRIPT_HOME/triggers.json` |
|
|
23
23
|
| Skills directory (`skillsDir`) | `$SKILLSCRIPT_HOME/skills/` |
|
|
24
24
|
| Sqlite skill store dbPath | `$SKILLSCRIPT_HOME/skills/skills.db` |
|
|
25
|
-
| Sqlite
|
|
25
|
+
| Sqlite data store dbPath | `$SKILLSCRIPT_HOME/data.db` |
|
|
26
26
|
| Trace directory | `$SKILLSCRIPT_HOME/traces/` |
|
|
27
27
|
|
|
28
28
|
`SKILLSCRIPT_HOME` defaults to `~/.skillscript`. Set the env var to relocate **everything** under a different root — the cleanest multi-instance isolation primitive:
|
|
@@ -47,7 +47,7 @@ A typical out-of-the-box `~/.skillscript/connectors.json`:
|
|
|
47
47
|
{
|
|
48
48
|
"substrate": {
|
|
49
49
|
"skill_store": "filesystem",
|
|
50
|
-
"
|
|
50
|
+
"data_store": "sqlite",
|
|
51
51
|
"local_model": null
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -88,7 +88,7 @@ Valid short-form values per slot:
|
|
|
88
88
|
| Slot | Values |
|
|
89
89
|
|---|---|
|
|
90
90
|
| `skill_store` | `"filesystem"` \| `"sqlite"` |
|
|
91
|
-
| `
|
|
91
|
+
| `data_store` | `"sqlite"` |
|
|
92
92
|
| `local_model` | (none — `"ollama"` requires the object form with `defaultModelTag`; see below) |
|
|
93
93
|
|
|
94
94
|
### Null — explicit "no substrate"
|
|
@@ -116,10 +116,10 @@ The runtime doesn't register a connector for this slot. Useful for explicitly di
|
|
|
116
116
|
|---|---|
|
|
117
117
|
| `filesystem` (skill_store) | none — uses the CLI's `skillsDir` (defaults to `$SKILLSCRIPT_HOME/skills/`) |
|
|
118
118
|
| `sqlite` (skill_store) | `dbPath` (default: `$SKILLSCRIPT_HOME/skills/skills.db`) |
|
|
119
|
-
| `sqlite` (
|
|
119
|
+
| `sqlite` (data_store) | `dbPath` (default: `$SKILLSCRIPT_HOME/data.db`; `DATA_DB` env overrides) |
|
|
120
120
|
| `ollama` (local_model) | `baseUrl` (default: `OLLAMA_BASE_URL` env or `http://localhost:11434`), **`defaultModelTag` (required — e.g., `"gemma2:9b"`, `"llama3.1:8b"`)** |
|
|
121
121
|
|
|
122
|
-
> **`
|
|
122
|
+
> **`SqliteDataStore` feature surface.** The bundled `sqlite` data_store is a deliberately minimal reference implementation: `supports_writes` + `supports_tag_filter` are true; `supports_semantic`, `supports_pinning`, `supports_decay_model`, `supports_thread_status_filter` are all false. Rich features (semantic retrieval, pinning, decay scoring, thread-status workflow) come from substrate impls — adopters fork `examples/connectors/DataStoreTemplate/` and wire their backing system (memory broker, vector DB, AMP, etc.). The bundled impl exists so the runtime works out-of-box; adopters with richer query semantics write their own.
|
|
123
123
|
|
|
124
124
|
Worked Ollama example (because the short form isn't valid for `local_model`):
|
|
125
125
|
|
|
@@ -163,7 +163,7 @@ When multiple config sources speak:
|
|
|
163
163
|
|
|
164
164
|
1. **Programmatic opts** (`opts.skillStore` passed to `bootstrap()`) — explicit, highest priority
|
|
165
165
|
2. **`connectors.json` substrate section** — declarative, deployment-durable
|
|
166
|
-
3. **Built-in default** — fallback (filesystem skill_store; conditional sqlite
|
|
166
|
+
3. **Built-in default** — fallback (filesystem skill_store; conditional sqlite data_store; no local_model)
|
|
167
167
|
|
|
168
168
|
If two configs disagree, the higher-priority one wins; lower-priority is ignored without error.
|
|
169
169
|
|
|
@@ -256,7 +256,7 @@ Underscore-prefixed top-level keys (`_comment`, `_note_security`, etc.) are igno
|
|
|
256
256
|
|
|
257
257
|
## Adopter-custom substrate impls
|
|
258
258
|
|
|
259
|
-
Write `class FooSkillStore implements SkillStore { ... }` (or
|
|
259
|
+
Write `class FooSkillStore implements SkillStore { ... }` (or DataStore, LocalModel). Wire it via either:
|
|
260
260
|
|
|
261
261
|
**(a) Programmatic bootstrap (recommended today)** — write your own bootstrap script that constructs the registry directly:
|
|
262
262
|
|
|
@@ -4,7 +4,7 @@ The substrate-neutral contracts skillscript-runtime exposes for adopters to wire
|
|
|
4
4
|
|
|
5
5
|
**Audience**: this doc is written for the agent that's implementing an adopter's AgentConnector — typically an LLM-class agent supervised by a human. If you're a human reading it directly, the same content applies; the prose is tightened for agent comprehension (literal field semantics, explicit precedence rules, worked examples).
|
|
6
6
|
|
|
7
|
-
Other contracts (McpConnector, SkillStore,
|
|
7
|
+
Other contracts (McpConnector, SkillStore, DataStore, LocalModel) audit + lock in subsequent v0.9.x slots; this doc grows with each lock.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -161,9 +161,9 @@ These are the load-bearing semantic rules. Internalize before implementing.
|
|
|
161
161
|
|
|
162
162
|
---
|
|
163
163
|
|
|
164
|
-
## Storage-layer conventions (SkillStore +
|
|
164
|
+
## Storage-layer conventions (SkillStore + DataStore)
|
|
165
165
|
|
|
166
|
-
The cold-adopter Phase 3 dogfood (writing AmpSkillStore +
|
|
166
|
+
The cold-adopter Phase 3 dogfood (writing AmpSkillStore + AmpDataStore against AMP) surfaced several conventions that live in the bundled reference impls but aren't first-class in the typed contracts. Adopters writing their own SkillStore/DataStore impls need to know about these, or skills/memories misbehave silently.
|
|
167
167
|
|
|
168
168
|
### SkillStore conventions
|
|
169
169
|
|
|
@@ -173,18 +173,18 @@ The cold-adopter Phase 3 dogfood (writing AmpSkillStore + AmpMemoryStore against
|
|
|
173
173
|
|
|
174
174
|
**`store()` auto-stamps the approval token.** When a caller writes a skill with `# Status: Approved` (no `vN:<token>`), the store MUST stamp the v1 CRC32 (or whatever version is currently `setPreferredApprovalVersion`'d) onto the body before persisting. Otherwise the skill is non-executable — the approval gate refuses bodies without a valid token. This is a **runtime-semantic concern that leaked into the storage layer**: ideally the runtime would stamp at execute-time, but for forward-compat reasons the convention is store-side stamping. See `src/approval.ts` and the v0.9.x hash-token auth thread.
|
|
175
175
|
|
|
176
|
-
###
|
|
176
|
+
### DataStore conventions
|
|
177
177
|
|
|
178
|
-
**`summary`/`detail` split is convention, not contract field.** The
|
|
178
|
+
**`summary`/`detail` split is convention, not contract field.** The DataStore contract gives `write()` a single `content: string`. Bundled `SqliteDataStore` maps this to `summary = first line (≤200 chars)` and `detail = full content`. Adopter substrates with native summary/detail concepts (AMP's `summary` + `detail` columns) can pre-compose and pass via `metadata`, but the basic mapping convention is "first line is the preview." Diverge and the dashboard's memory rendering looks weird, but skills still work.
|
|
179
179
|
|
|
180
|
-
**`get(id)` returns null on miss, doesn't throw.** Distinct from SkillStore's `load(name)` which throws `SkillNotFoundError`.
|
|
180
|
+
**`get(id)` returns null on miss, doesn't throw.** Distinct from SkillStore's `load(name)` which throws `SkillNotFoundError`. DataStore's empty-set convention (`query()` returns `[]` not throws; `get()` returns `null` not throws) is **load-bearing for the runtime's control flow** — query callers branch on `result.length`, get callers branch on `result === null`. Don't change this in your impl. Per cold agent's "credit where due": *"unambiguous, and the runtime keys control flow on the specific classes."*
|
|
181
181
|
|
|
182
182
|
### Durability stance (both contracts)
|
|
183
183
|
|
|
184
|
-
**The typed contracts assume durable storage.** Neither SkillStore nor
|
|
184
|
+
**The typed contracts assume durable storage.** Neither SkillStore nor DataStore declare "writes live forever" anywhere in the interface — but the runtime + lint + dashboard all behave as if writes persist indefinitely. Substrate backends with their own GC / TTL / decay scoring surprise the skillscript layer invisibly:
|
|
185
185
|
|
|
186
186
|
- A skill written to a substrate that auto-expires after N days disappears from `skill_list` without warning
|
|
187
|
-
- A memory written with an implicit TTL gets pruned, breaking later `$
|
|
187
|
+
- A memory written with an implicit TTL gets pruned, breaking later `$ data_read query` references
|
|
188
188
|
- A substrate that pin-deletes stale content silently invalidates persisted skill references
|
|
189
189
|
|
|
190
190
|
Implementer responsibility: either pick a substrate posture that satisfies "durable forever," or build adopter-side guards (e.g., pin-rules, retention policies, periodic re-pin sweeps) that maintain the assumption. The contract doesn't enforce — silent staleness is the failure mode.
|
|
@@ -193,7 +193,7 @@ Implementer responsibility: either pick a substrate posture that satisfies "dura
|
|
|
193
193
|
|
|
194
194
|
**Filters are advisory at the contract layer (v0.13.x and earlier).** `query(filters)` accepts arbitrary fields via the `[key: string]: unknown` extension; substrates honor what they support and silently drop the rest. For non-scope-sensitive filters (`tag`, `since`) this is fine. For **scope/visibility/security-relevant filters** (any future `vault` filter, tenant-id, access-control) — the silent-drop is a leak waiting to happen.
|
|
195
195
|
|
|
196
|
-
Today's discipline: enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/
|
|
196
|
+
Today's discipline: enforce scope-relevant filters *above* the contract — adopter wraps the SkillStore/DataStore with a guard that asserts the substrate honors the filter, or the wrapping layer applies the filter in-memory after the substrate returns.
|
|
197
197
|
|
|
198
198
|
v0.14.0 plans `strict_filters: true` at the contract layer so the substrate must surface unsupported-filter use via `UnsupportedFilterError`. Until then, adopter-side enforcement is the only option.
|
|
199
199
|
|
|
@@ -4,9 +4,6 @@ Canonical language reference for skillscript. Audience: skill authors (human + a
|
|
|
4
4
|
|
|
5
5
|
Implementation state is cross-referenced to commit hashes; pending items mark v2/v3 work.
|
|
6
6
|
|
|
7
|
-
Companion docs under the Skillscript project anchor:
|
|
8
|
-
- `skillscript-prd` — product positioning, value prop, roadmap
|
|
9
|
-
- `skillscript-erd` — engineering requirements, system architecture, runtime mechanics
|
|
10
7
|
|
|
11
8
|
## Not yet implemented, but planned
|
|
12
9
|
|
|
@@ -103,7 +100,7 @@ Skillscript is a constrained domain-specific language for authoring agent workfl
|
|
|
103
100
|
Every skill follows the same shape:
|
|
104
101
|
|
|
105
102
|
1. **Trigger** — what fires the skill: cron, command, session-start, agent-event, file-watch, webhook, etc.
|
|
106
|
-
2. **Process** — pull data (MCP /
|
|
103
|
+
2. **Process** — pull data (MCP / data store / file), classify or compose via sub-LLM + iteration, build the deliverable.
|
|
107
104
|
3. **Deliver** — emit the result via one or more delivery channels (see below).
|
|
108
105
|
|
|
109
106
|
Skillscript's job is to express this pipeline declaratively. When there is an agent above the skill, the agent's job is to act on the delivered artifact. When there isn't (autonomous fires), the delivery channel IS the outcome.
|
|
@@ -128,9 +125,9 @@ A skill delivers its work via one or more of three channels. Delivery channel is
|
|
|
128
125
|
|---|---|---|
|
|
129
126
|
| **Augmenting (context to agent)** | `emit(text="...")` + `# Output: agent: <name>` | Skill output is augment-kind payload for the receiving agent's next turn; joined emit stream becomes the delivered context. Pattern: agent-augmenting skills (briefing skills, session-start prepared context). |
|
|
130
127
|
| **Template (playbook to agent)** | `emit(text="...")` + `# Output: template: <name>` | Skill output is a template-kind payload (recipe/playbook) the receiving agent executes. Pattern: instructional skills, reusable recipes. |
|
|
131
|
-
| **
|
|
128
|
+
| **Data handoff** | `$ data_write content="..." recipients=[<agent>] -> R` | Skill writes data the target agent picks up via mailbox at next session. Pattern: async carrier skills, autonomous fires that hand off to a future session. |
|
|
132
129
|
|
|
133
|
-
A single skill can use any combination. An autonomous cron-fired sweep might write
|
|
130
|
+
A single skill can use any combination. An autonomous cron-fired sweep might write data to one agent AND emit augment-kind context to another. The combinations are unconstrained — the per-op gating model governs which mutating ops are authorized, not which channels a skill uses.
|
|
134
131
|
|
|
135
132
|
## Three op classes
|
|
136
133
|
|
|
@@ -148,19 +145,19 @@ Full op catalog and per-op semantics in Ops Reference.
|
|
|
148
145
|
|
|
149
146
|
## Substrate portability
|
|
150
147
|
|
|
151
|
-
The language doesn't privilege any backend. `$ llm`, `$
|
|
148
|
+
The language doesn't privilege any backend. `$ llm`, `$ data_read`, `$ ticketing_search` are not language built-ins — they're connector names resolved at runtime through the registered MCP connector instances. The same skill source runs against any conforming substrate.
|
|
152
149
|
|
|
153
150
|
The runtime ships bundled bridges for two common patterns:
|
|
154
151
|
|
|
155
152
|
- `$ llm` routes through whichever LocalModel is wired (via `substrate.local_model` in `connectors.json`)
|
|
156
|
-
- `$
|
|
153
|
+
- `$ data_read` / `$ data_write` route through whichever DataStore is wired (via `substrate.data_store`)
|
|
157
154
|
|
|
158
155
|
Adopters wire OpenAI instead of Ollama, Pinecone instead of SQLite, etc. Configuration lives outside the skill body; the language remains agnostic.
|
|
159
156
|
|
|
160
157
|
| Connector slot | Adopter A wires | Adopter B wires |
|
|
161
158
|
|---|---|---|
|
|
162
159
|
| `llm` | LocalModel: Ollama (default) | LocalModel: OpenAI |
|
|
163
|
-
| `
|
|
160
|
+
| `data_read` / `data_write` | DataStore: SqliteDataStore (default) | DataStore: Pinecone |
|
|
164
161
|
| `ticketing_search` | YouTrack MCP | Jira MCP |
|
|
165
162
|
|
|
166
163
|
Substrate config syntax + the three-form configuration shape lives in the adopter playbook, not in this reference. Skill authors don't typically need to touch it — they author against the canonical `$ tool` surfaces and adopters wire whatever's underneath.
|
|
@@ -201,7 +198,7 @@ The joined `emit()` stream becomes the augment-kind payload delivered to the on-
|
|
|
201
198
|
2. **Targets** — named blocks of typed ops, optionally with `needs:` dependencies
|
|
202
199
|
3. **`default:`** — names the goal target the runtime walks toward
|
|
203
200
|
|
|
204
|
-
Other delivery channels for the same shape: swap `# Output: agent: oncall` + the `emit()` calls for `$
|
|
201
|
+
Other delivery channels for the same shape: swap `# Output: agent: oncall` + the `emit()` calls for `$ data_write content="..." recipients=["oncall"] approved="cron-fired" -> R` (one summary data handoff per fire) or `file_write(path="/var/log/showstoppers-${EVENT.fired_at_unix}.md", content="...", approved="cron-fired")` (one file deliverable per fire).
|
|
205
202
|
|
|
206
203
|
## Lexical conventions
|
|
207
204
|
|
|
@@ -476,14 +473,14 @@ Both forms are first-class. Neither is canonical; choose by what makes the call
|
|
|
476
473
|
```
|
|
477
474
|
$ ticketing_search query="project:INFRA" -> R
|
|
478
475
|
$ llm prompt="${INPUT}" -> V
|
|
479
|
-
$
|
|
476
|
+
$ data_read mode=fts query="..." limit=5 -> M
|
|
480
477
|
```
|
|
481
478
|
|
|
482
479
|
**Dotted-prefix dispatch** is the explicit-routing escape hatch — useful when multiple connectors expose tools with overlapping names, or when an adopter wants the connector identity visible at the call site for audit clarity:
|
|
483
480
|
|
|
484
481
|
```
|
|
485
482
|
$ ticketing.search query="project:INFRA" -> R
|
|
486
|
-
$
|
|
483
|
+
$ data_read.query query="..." -> M
|
|
487
484
|
```
|
|
488
485
|
|
|
489
486
|
Parser rule: the text before the dot is the connector name (must match an entry in `connectors.json`); the rest is the tool + args.
|
|
@@ -493,13 +490,13 @@ Parser rule: the text before the dot is the connector name (must match an entry
|
|
|
493
490
|
```
|
|
494
491
|
$ ticketing_search query="project:INFRA state:Open" limit=20 -> ISSUES
|
|
495
492
|
$ llm prompt="Classify: ${INPUT}" -> VERDICT
|
|
496
|
-
$
|
|
497
|
-
$
|
|
493
|
+
$ data_read mode=fts query="${TOPIC}" limit=5 -> RESULTS
|
|
494
|
+
$ data_write content="${SUMMARY}" recipients=["oncall"] approved="morning roundup, 2026-05-25" -> ACK
|
|
498
495
|
```
|
|
499
496
|
|
|
500
497
|
Tool args are unconstrained `key=value` pairs — the connector forwards them to the underlying MCP tool. If a dispatched call returns `isError: true`, the executor throws via `makeOpError`, which routes through `else:` / `# OnError:` machinery. The inner tool's error text is preserved in `result.errors[]`.
|
|
501
498
|
|
|
502
|
-
**Substrate-neutrality.** Connector names like `$ llm`, `$
|
|
499
|
+
**Substrate-neutrality.** Connector names like `$ llm`, `$ data_read`, `$ ticketing_search` are NOT reserved or built-in — they're whatever the adopter declares in `connectors.json` (substrate config). Bridges for `$ llm` and `$ data_read` / `$ data_write` auto-wire only when the adopter's substrate config sets `substrate.local_model` / `substrate.data_store` respectively. See the adopter playbook for the full substrate config reference.
|
|
503
500
|
|
|
504
501
|
**Unknown connector** → tier-1 `unknown-connector` lint with the list of wired connector names.
|
|
505
502
|
|
|
@@ -513,7 +510,7 @@ Mutation ops require an authorization signal. The signal is per-op, not a mode b
|
|
|
513
510
|
|
|
514
511
|
**Mutation-classified ops:**
|
|
515
512
|
- `file_write(...)` (runtime-intrinsic)
|
|
516
|
-
- `$
|
|
513
|
+
- `$ data_write ...` and any MCP connector entry declared `"mutating": true` in `connectors.json`
|
|
517
514
|
- `shell(command=..., unsafe=true)` (always mutation-classified)
|
|
518
515
|
- `shell(command=...)` with destructive verb (rm, mv, dd, mkfs, etc. — heuristic list)
|
|
519
516
|
- `$ <tool>` matching the mutating-verb regex
|
|
@@ -537,7 +534,7 @@ Mutation ops require an authorization signal. The signal is per-op, not a mode b
|
|
|
537
534
|
|
|
538
535
|
deliver:
|
|
539
536
|
file_write(path="/tmp/sweep.md", content="${REPORT}") # no approved= needed
|
|
540
|
-
$
|
|
537
|
+
$ data_write content="${REPORT}" recipients=["oncall"] # no approved= needed
|
|
541
538
|
```
|
|
542
539
|
|
|
543
540
|
```
|
|
@@ -577,35 +574,15 @@ flush:
|
|
|
577
574
|
| Runtime-intrinsic | `file_write` | `file_write(path="...", content="...", [approved="..."])` | none |
|
|
578
575
|
| External MCP | `$ <connector>` | `$ <name>[.<tool>] kwarg=value, ... [-> R]` | optional |
|
|
579
576
|
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
## Legacy syntax (deprecated, grace period)
|
|
583
|
-
|
|
584
|
-
These symbol-forms shipped in earlier versions and still compile during the grace period with tier-2 `deprecated-symbol-op` warnings. New skills should use the canonical forms.
|
|
585
|
-
|
|
586
|
-
| Deprecated | Canonical replacement |
|
|
587
|
-
|---|---|
|
|
588
|
-
| `~ prompt="..."` | `$ llm prompt="..."` |
|
|
589
|
-
| `> mode=... query=...` | `$ memory mode=... query=...` |
|
|
590
|
-
| `@ <command>` | `shell(command="...")` |
|
|
591
|
-
| `@ unsafe <command>` | `shell(command="...", unsafe=true)` |
|
|
592
|
-
| `! <text>` | `emit(text="<text>")` |
|
|
593
|
-
| `?? "<prompt>" -> R` | `ask(prompt="<prompt>") -> R` |
|
|
594
|
-
| `& <data-skill-name>` | `inline(skill="<data-skill-name>")` |
|
|
595
|
-
| `$(VAR)` (legacy substitution) | `${VAR}` |
|
|
596
|
-
| `(approved: "reason")` trailer | `approved="reason"` kwarg |
|
|
597
|
-
|
|
598
|
-
The symbol-per-op design was deprecated when verb-word ops in function-call shape proved more author-friendly (training-corpus alignment + human-reviewability). Removal lands in a future version; until then, the canonical surface is the recommended form.
|
|
599
|
-
|
|
600
577
|
## Variable resolution — ${VAR} canonical, substitution + ambient refs + # Requires: cascade
|
|
601
578
|
|
|
602
|
-
Skillscript supports four tiers of variables, each with distinct resolution timing and scope. Substitution uses **`${VAR}` as the canonical form**.
|
|
579
|
+
Skillscript supports four tiers of variables, each with distinct resolution timing and scope. Substitution uses **`${VAR}` as the canonical form**.
|
|
603
580
|
|
|
604
581
|
## Substitution syntax — `${VAR}` canonical
|
|
605
582
|
|
|
606
583
|
```
|
|
607
584
|
emit(text="Hello, ${USER.login}!")
|
|
608
|
-
$
|
|
585
|
+
$ data_read mode=fts query="${TOPIC}" limit=5 -> R
|
|
609
586
|
$set REPORT = "Triage for ${PROJECT} (${ISSUES|length} open):\n"
|
|
610
587
|
```
|
|
611
588
|
|
|
@@ -638,7 +615,7 @@ For cron and session triggers, the scheduler injects time-offset ambient fields
|
|
|
638
615
|
|
|
639
616
|
These let skill bodies compute `expires_at` and similar bounded-lifetime values without arithmetic in op kwargs. For ISO-formatted rendering of any epoch value, see the `|isodate` filter.
|
|
640
617
|
|
|
641
|
-
Additional ambient refs may be injected based on connector configuration
|
|
618
|
+
Additional ambient refs may be injected based on connector configuration. Connector documentation specifies which ambient refs each connector contributes.
|
|
642
619
|
|
|
643
620
|
## Tier 2: Input
|
|
644
621
|
|
|
@@ -662,18 +639,16 @@ Bracketed list literals supported (`# Vars: TAGS=[a, b, c]`).
|
|
|
662
639
|
|
|
663
640
|
## Tier 4: Local
|
|
664
641
|
|
|
665
|
-
Bound to a previous target's output mid-execution.
|
|
642
|
+
Bound to a previous target's output mid-execution. Three forms:
|
|
666
643
|
- `${target.output}` — the bound output of a target
|
|
667
644
|
- `${VAR}` — an explicit `-> VAR` binding from any op
|
|
668
|
-
- `${
|
|
645
|
+
- `${VAR.field}` / `${VAR.nested.field}` — dotted field access into structured output
|
|
669
646
|
|
|
670
|
-
|
|
671
|
-
1. Core `PortableMemory` fields (id, summary, detail, score)
|
|
672
|
-
2. Curated substrate subset (thread_status, pinned, confidence, domain_tags, payload_type, knowledge_type, recipients, expires_at, created_at, agent_id, vault)
|
|
673
|
-
3. `metadata.X` for everything else
|
|
674
|
-
4. Ambient passthrough as literal `${MEMORY.field}` if unresolved
|
|
647
|
+
The exact field set available on a bound variable depends on what the wired connector returns. Field access resolves against the returned object's structure. If a field doesn't resolve, the substitution errors unless `|fallback:"default"` is supplied (see Pipe filters for full `|fallback:` semantics):
|
|
675
648
|
|
|
676
|
-
|
|
649
|
+
```
|
|
650
|
+
emit(text="Assignee: ${ISSUE.customFields.Assignee|fallback:\"unassigned\"}")
|
|
651
|
+
```
|
|
677
652
|
|
|
678
653
|
## Resolution order
|
|
679
654
|
|
|
@@ -751,7 +726,7 @@ Strings that hold JSON arrays get the same tolerance as `in`/`not in` RHS: if th
|
|
|
751
726
|
Pairs naturally with the numeric comparison operators (see Conditionals section):
|
|
752
727
|
|
|
753
728
|
```
|
|
754
|
-
$
|
|
729
|
+
$ data_read mode=fts query="urgent" -> ITEMS
|
|
755
730
|
if ${ITEMS|length} > 5:
|
|
756
731
|
emit(text="Mailbox is getting crowded")
|
|
757
732
|
```
|
|
@@ -901,7 +876,7 @@ The ref-vs-ref form is the canonical change-detection pattern. Both sides resolv
|
|
|
901
876
|
if ${M.id|trim} in ${SEEN}:
|
|
902
877
|
emit(text="already processed")
|
|
903
878
|
elif ${M.id} not in ${SEEN}:
|
|
904
|
-
$
|
|
879
|
+
$ data_write content="..." approved="dedup" -> R
|
|
905
880
|
```
|
|
906
881
|
|
|
907
882
|
Both sides are explicit refs. RHS must resolve to an array at runtime; clean error otherwise. LHS-undefined evaluates to `false` for both polarities. Optional filter on LHS.
|
|
@@ -909,9 +884,9 @@ Both sides are explicit refs. RHS must resolve to an array at runtime; clean err
|
|
|
909
884
|
**JSON-string tolerance on RHS**: if the RHS resolves to a *string* that successfully JSON-parses to an array, the parsed array is used. This accommodates the canonical pattern where the array comes from a `$ llm` call that prompted for JSON output:
|
|
910
885
|
|
|
911
886
|
```
|
|
912
|
-
$ llm prompt="List the URGENT
|
|
887
|
+
$ llm prompt="List the URGENT item IDs as a JSON array of strings. Items: ${M|json}" -> SEEN
|
|
913
888
|
|
|
914
|
-
foreach M in ${
|
|
889
|
+
foreach M in ${ITEMS}:
|
|
915
890
|
if ${M.id} in ${SEEN}:
|
|
916
891
|
emit(text="flagged urgent")
|
|
917
892
|
```
|
|
@@ -932,7 +907,7 @@ elif ${COUNT} <= 0:
|
|
|
932
907
|
Filters and dotted-field access work on either side, same as equality. The `|length` filter (see Pipe filters section) is the canonical companion — `${LIST|length} > 5` is the natural "more than five items" pattern:
|
|
933
908
|
|
|
934
909
|
```
|
|
935
|
-
$
|
|
910
|
+
$ data_read mode=fts query="urgent" -> ITEMS
|
|
936
911
|
if ${ITEMS|length} > 5:
|
|
937
912
|
emit(text="Mailbox is getting crowded")
|
|
938
913
|
```
|
|
@@ -972,7 +947,7 @@ classify:
|
|
|
972
947
|
|
|
973
948
|
```
|
|
974
949
|
mailbox_check:
|
|
975
|
-
$
|
|
950
|
+
$ data_read mode=fts query="addressed:perry" limit=10 -> MAILBOX
|
|
976
951
|
if not ${MAILBOX}:
|
|
977
952
|
emit(text="empty mailbox today")
|
|
978
953
|
elif ${MAILBOX|length} > 5:
|
|
@@ -1019,12 +994,12 @@ Both can coexist in the same target.
|
|
|
1019
994
|
foreach M in ${RESULTS}:
|
|
1020
995
|
emit(text="Processing ${M.id} — ${M.summary}")
|
|
1021
996
|
if ${M.id|trim} not in ${SEEN}:
|
|
1022
|
-
$
|
|
997
|
+
$ data_write content="${M.summary}" approved="dedup" -> ACK
|
|
1023
998
|
```
|
|
1024
999
|
|
|
1025
1000
|
### Iterator vars
|
|
1026
1001
|
|
|
1027
|
-
`${M}` and `${M.field}` pass through ambient at compile; runtime substitutes per iteration. Dotted field access against
|
|
1002
|
+
`${M}` and `${M.field}` pass through ambient at compile; runtime substitutes per iteration. Dotted field access resolves against whatever structure the connector returned per item — the exact field set depends on the connector. Indexed access (`${LIST.0}`, `${LIST.0.id}`) also works on bound results.
|
|
1028
1003
|
|
|
1029
1004
|
### Loop-local scope (and the accumulator exception)
|
|
1030
1005
|
|
|
@@ -1369,7 +1344,7 @@ Runs if any op in the target's primary body errors. Local to the failing target.
|
|
|
1369
1344
|
|
|
1370
1345
|
```
|
|
1371
1346
|
fetch:
|
|
1372
|
-
$
|
|
1347
|
+
$ data_read mode=fts query=${TOPIC} limit=5 -> RESULT
|
|
1373
1348
|
else:
|
|
1374
1349
|
emit(text="retrieval failed, falling back to empty result")
|
|
1375
1350
|
$set RESULT = ""
|
|
@@ -1412,7 +1387,7 @@ Inline fallback declared on the op line. Used when the call fails or returns emp
|
|
|
1412
1387
|
|
|
1413
1388
|
```
|
|
1414
1389
|
weather:
|
|
1415
|
-
$
|
|
1390
|
+
$ data_read mode=fts query="weather ${LOCATION}" limit=1 -> CURRENT (fallback: "weather unavailable")
|
|
1416
1391
|
$ llm prompt="Summarize: ${CURRENT}" -> SUMMARY (fallback: "summary unavailable")
|
|
1417
1392
|
$ slack.post channel=${CHANNEL} text=${SUMMARY} (fallback: "post failed silently") -> ACK
|
|
1418
1393
|
```
|
|
@@ -1422,7 +1397,7 @@ Same pattern as the `# Requires:` cascade's `(fallback: ...)` syntax — consist
|
|
|
1422
1397
|
**Fallback value parsing.** Permissive: bare identifiers, quoted strings, and bracketed array literals all accepted. Matches the `# Requires:` cascade convention.
|
|
1423
1398
|
|
|
1424
1399
|
```
|
|
1425
|
-
$
|
|
1400
|
+
$ data_read mode=fts query="..." -> RESULTS (fallback: []) # array literal
|
|
1426
1401
|
$ llm prompt="..." -> VERDICT (fallback: unknown) # bare identifier
|
|
1427
1402
|
$ slack.post text="..." -> ACK (fallback: "post failed") # quoted string
|
|
1428
1403
|
```
|
|
@@ -1728,7 +1703,7 @@ Normal `prompt` / `prose` compilation ignores the `# Tests:` section entirely
|
|
|
1728
1703
|
|
|
1729
1704
|
### Runtime assertion sandboxing
|
|
1730
1705
|
|
|
1731
|
-
`# Tests:` cases that exercise runtime behavior (
|
|
1706
|
+
`# Tests:` cases that exercise runtime behavior (data writes, shell ops, LocalModel calls) need a sandbox so they don't pollute production data. Two approaches:
|
|
1732
1707
|
- Scratch DB / scratch connector overrides for tests
|
|
1733
1708
|
- Skip-and-warn for non-deterministic ops, only assert deterministic compile-time properties
|
|
1734
1709
|
|
|
@@ -1832,7 +1807,7 @@ $set NAME = value scope=session
|
|
|
1832
1807
|
|
|
1833
1808
|
Backed by a configured data-records connector (the same surface `# Requires:` reads from) with conventionally-namespaced keys (e.g., `state:skill-local:<skill-name>:<key>`).
|
|
1834
1809
|
|
|
1835
|
-
**Rationale:** Most interesting skills need
|
|
1810
|
+
**Rationale:** Most interesting skills need persistent state across firings — change-detection, windowing, dedup-against-recent. Without lifecycle, every skill rebuilds state tracking via raw `$ data_write` / `$ data_read` calls.
|
|
1836
1811
|
|
|
1837
1812
|
## Cross-skill pub-sub
|
|
1838
1813
|
|
|
@@ -1905,7 +1880,7 @@ ${SELF.confidence-trend}
|
|
|
1905
1880
|
Skill declares its required surfaces:
|
|
1906
1881
|
|
|
1907
1882
|
```
|
|
1908
|
-
# Requires-Capabilities: sensors=[mic, camera], tools=[
|
|
1883
|
+
# Requires-Capabilities: sensors=[mic, camera], tools=[datastore.write, slack.post]
|
|
1909
1884
|
# Requires-Privacy: private-channel-only
|
|
1910
1885
|
```
|
|
1911
1886
|
|
|
@@ -1968,5 +1943,5 @@ Hung dispatches hang the skill without explicit timeout configuration. Lean: ski
|
|
|
1968
1943
|
|
|
1969
1944
|
---
|
|
1970
1945
|
|
|
1971
|
-
*Rendered from `skillscript/skillscript-language-reference` — 2026-05-
|
|
1946
|
+
*Rendered from `skillscript/skillscript-language-reference` — 2026-05-30 09:52 EDT*
|
|
1972
1947
|
*Source of truth: AMP (`amp_render_document("skillscript/skillscript-language-reference")`)*
|
|
@@ -159,7 +159,7 @@ registerApprovalFn("v2", (body) => hmacSha256(SECRET, body));
|
|
|
159
159
|
When forking into your codebase:
|
|
160
160
|
|
|
161
161
|
1. Rename the class (e.g., `PostgresSkillStore`, `AmpSkillStore`)
|
|
162
|
-
2. Replace the SQL with your substrate's API (HTTP,
|
|
162
|
+
2. Replace the SQL with your substrate's API (HTTP, DataStore, vector DB, etc.)
|
|
163
163
|
3. Update `staticCapabilities()` to match what your substrate actually supports — drop `supports_versioning` if you can't track history, drop `supports_tag_filter` if querying tags isn't tractable
|
|
164
164
|
4. Update `manifest()` to describe your substrate (`kind: "amp"` or whatever)
|
|
165
165
|
5. Tests: copy `tests/SqliteSkillStore.test.ts` as a starting point + run the conformance suite (`SkillStoreConformance.buildTests()` from `skillscript-runtime/testing`)
|
package/examples/README.md
CHANGED
|
@@ -9,9 +9,9 @@ For full language semantics, see [`../docs/language-reference.md`](../docs/langu
|
|
|
9
9
|
| [`hello.skill.md`](./skillscripts/hello.skill.md) | Three-command first run; `! emit`; default target; `# Vars:` declared inputs |
|
|
10
10
|
| [`morning-brief.skill.md`](./skillscripts/morning-brief.skill.md) | Multi-target with `needs:`; cron-fired with `EVENT.fired_at_*`; `# OnError:` fallback; dual `# Output:` (slack + prompt-context); `# Requires:` user-var cascade with fallback; LocalModel + retrieval composition |
|
|
11
11
|
| [`doc-qa-with-citations.skill.md`](./skillscripts/doc-qa-with-citations.skill.md) | Single-target retrieval; `(fallback: [])` op-level fallback; LLM-with-citation pattern; pipe filter `\|json` for prompt embedding |
|
|
12
|
-
| [`classify-support-ticket.skill.md`](./skillscripts/classify-support-ticket.skill.md) | `if`/`elif`/`else` multi-branch routing; classifier-cascade pattern;
|
|
12
|
+
| [`classify-support-ticket.skill.md`](./skillscripts/classify-support-ticket.skill.md) | `if`/`elif`/`else` multi-branch routing; classifier-cascade pattern; DataStore `$` writes with structured `domain_tags`; `$set` literal binding |
|
|
13
13
|
| [`cut-release-tag.skill.md`](./skillscripts/cut-release-tag.skill.md) | `??` interactive ask-for-input; `else:` short-circuit on multiple targets; `@` shell ops with per-op error fallback; rollback-on-failure flow |
|
|
14
|
-
| [`service-health-watch.skill.md`](./skillscripts/service-health-watch.skill.md) | `foreach` iteration; cron-fired with `expires_at=$(EVENT.fired_at_plus_1d_unix)` TTL math; classifier-gated
|
|
14
|
+
| [`service-health-watch.skill.md`](./skillscripts/service-health-watch.skill.md) | `foreach` iteration; cron-fired with `expires_at=$(EVENT.fired_at_plus_1d_unix)` TTL math; classifier-gated DataStore writes; pipe filter `\|url` for path-safe interpolation |
|
|
15
15
|
| [`feedback-sentiment-scan.skill.md`](./skillscripts/feedback-sentiment-scan.skill.md) | `in` / `not in` set-membership ops; dedupe-via-seen-markers idiom; nested `if`/`elif` classification cascade; TTL on bookkeeping writes |
|
|
16
16
|
|
|
17
17
|
## Connector requirements
|
|
@@ -19,9 +19,9 @@ For full language semantics, see [`../docs/language-reference.md`](../docs/langu
|
|
|
19
19
|
Most examples assume the bundled-default connectors:
|
|
20
20
|
|
|
21
21
|
- `hello.skill.md` — zero deps (runs cold)
|
|
22
|
-
- `doc-qa-with-citations.skill.md` — Ollama (LocalModel) +
|
|
23
|
-
- `morning-brief.skill.md`, `feedback-sentiment-scan.skill.md` — Ollama +
|
|
24
|
-
- `classify-support-ticket.skill.md` — Ollama +
|
|
22
|
+
- `doc-qa-with-citations.skill.md` — Ollama (LocalModel) + DataStore for the retrieval cache
|
|
23
|
+
- `morning-brief.skill.md`, `feedback-sentiment-scan.skill.md` — Ollama + DataStore + a calendar MCP connector (the `$ calendar.list_events` op)
|
|
24
|
+
- `classify-support-ticket.skill.md` — Ollama + DataStore
|
|
25
25
|
- `cut-release-tag.skill.md` — git CLI on `PATH`; expects to run inside a git repo
|
|
26
26
|
- `service-health-watch.skill.md` — `curl` on `PATH`; network egress to `status.internal/*`
|
|
27
27
|
|