skillscript-runtime 0.19.15 → 0.20.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 +35 -2
- package/dist/approval.d.ts +80 -54
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +156 -120
- package/dist/approval.js.map +1 -1
- package/dist/bootstrap.d.ts +41 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +59 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +245 -24
- package/dist/cli.js.map +1 -1
- package/dist/compile.js +7 -0
- package/dist/compile.js.map +1 -1
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +13 -1
- package/dist/composition.js.map +1 -1
- package/dist/connectors/skill-store.d.ts.map +1 -1
- package/dist/connectors/skill-store.js +41 -13
- package/dist/connectors/skill-store.js.map +1 -1
- package/dist/connectors/sqlite-skill-store.d.ts.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +21 -12
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/dashboard/spa/app.js +186 -8
- package/dist/dashboard/spa/index.html +2 -0
- package/dist/dashboard/spa/styles.css +42 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +49 -0
- package/dist/errors.js.map +1 -1
- package/dist/help-content.d.ts.map +1 -1
- package/dist/help-content.js +2 -0
- package/dist/help-content.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +24 -7
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +21 -4
- package/dist/mcp-server.js.map +1 -1
- package/dist/parser.js +2 -2
- package/dist/parser.js.map +1 -1
- package/dist/runtime-config.d.ts +6 -0
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +8 -0
- package/dist/runtime-config.js.map +1 -1
- package/dist/runtime-env-resolver.d.ts +4 -0
- package/dist/runtime-env-resolver.d.ts.map +1 -1
- package/dist/runtime-env-resolver.js +27 -0
- package/dist/runtime-env-resolver.js.map +1 -1
- package/dist/runtime.d.ts +18 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +84 -17
- package/dist/runtime.js.map +1 -1
- package/dist/safe-path.d.ts +22 -0
- package/dist/safe-path.d.ts.map +1 -1
- package/dist/safe-path.js +72 -1
- package/dist/safe-path.js.map +1 -1
- package/dist/scheduler.d.ts +3 -0
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +8 -0
- package/dist/scheduler.js.map +1 -1
- package/docs/adopter-playbook.md +190 -30
- package/docs/configuration.md +5 -1
- package/docs/connector-contract-reference.md +5 -1
- package/docs/language-reference.md +83 -130
- package/docs/sqlite-skill-store.md +5 -1
- package/examples/skillscripts/classify-support-ticket.skill.md +1 -1
- package/examples/skillscripts/data-store-roundtrip.skill.md +1 -1
- package/examples/skillscripts/doc-qa-with-citations.skill.md +1 -1
- package/examples/skillscripts/feedback-sentiment-scan.skill.md +1 -1
- package/examples/skillscripts/hello-world.skill.md +1 -1
- package/examples/skillscripts/hello-world.skill.provenance.json +1 -1
- package/examples/skillscripts/morning-brief.skill.md +1 -1
- package/examples/skillscripts/queue-length-monitor.skill.md +1 -1
- package/examples/skillscripts/service-health-watch.skill.md +1 -1
- package/examples/skillscripts/skill-store-roundtrip.skill.md +1 -1
- package/examples/skillscripts/youtrack-morning-sweep.skill.md +1 -1
- package/package.json +1 -1
- package/scaffold/skills/data-store-roundtrip.skill.md +1 -1
- package/scaffold/skills/hello-world.skill.md +1 -1
- package/scaffold/skills/skill-store-roundtrip.skill.md +1 -1
|
@@ -1,91 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Language Reference
|
|
3
|
+
description: "Canonical spec — syntax, ops, semantics. Frontmatter, ops, filters, conditionals, triggers, output kinds, composition, error handling."
|
|
4
|
+
mode: wide
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
Canonical language reference for skillscript. Audience: skill authors (human + agent). Specifies what is valid syntax, what behavior to expect at compile + runtime, and what is currently pending implementation.
|
|
4
8
|
|
|
5
9
|
Implementation state is cross-referenced to commit hashes; pending items mark v2/v3 work.
|
|
6
10
|
|
|
7
11
|
|
|
8
|
-
## Not yet implemented, but planned
|
|
9
|
-
|
|
10
|
-
These are features designed or anticipated but not yet implemented in the current build. Authors should not use these forms; they will not compile.
|
|
11
|
-
|
|
12
|
-
## Control flow
|
|
13
|
-
|
|
14
|
-
- **`while CONDITION:` loops** — today's iteration is `foreach IDENT in EXPR:` only. While loops are planned for ad-hoc orchestration patterns ("loop until response contains 'done'").
|
|
15
|
-
- **Arithmetic in `$set`** — today accepts literals + `${VAR}` interpolation; no `+ - * /` operators. Planned alongside `while` for turn counters and orchestration bookkeeping.
|
|
16
|
-
|
|
17
|
-
## Pipe filter extensions
|
|
18
|
-
|
|
19
|
-
- **Array aggregation primitives** — `|max`, `|min`, `|sum`, `|reduce` over arrays. Today the language tops out at "shape one record" — aggregating across an array requires `foreach` accumulator ceremony. Planned as a design question: is `foreach` the deliberate ceiling for aggregation, or do we add primitives?
|
|
20
|
-
|
|
21
|
-
## Triggers — note
|
|
22
|
-
|
|
23
|
-
Trigger sources are `cron` + `event` only (see the Triggers section). There is no separate `agent-event:` / `file-watch:` / `sensor:` trigger source — anything that isn't time-based is an external adapter that POSTs to the `/event` ingress. So there's nothing pending here on the trigger axis; the entry remains only to point readers who expect those sources at the adapter-POST pattern.
|
|
24
|
-
|
|
25
|
-
## Synchronous agent exchange
|
|
26
|
-
|
|
27
|
-
- **`exchange()` runtime-intrinsic op** — synchronous send + wait pattern for multi-agent conferences. Awaits adopter-substrate queue impl + AgentConnector contract grow.
|
|
28
|
-
|
|
29
|
-
## Tests
|
|
30
|
-
|
|
31
|
-
- **`# Tests:` block** with `given:` / `expect:` assertions — author-authored test cases. Will land when adopter signal demands test infrastructure.
|
|
32
|
-
|
|
33
|
-
## Output kinds
|
|
34
|
-
|
|
35
|
-
- **`# Output: file: <path>`** — file-output routing parses but no router exists today.
|
|
36
|
-
- **`# Output: card:`** — depends on a substrate-side card render surface; not implemented.
|
|
37
|
-
|
|
38
|
-
## Persistent state with declared scope
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
$set NAME = value scope=skill-local
|
|
42
|
-
$set NAME = value scope=agent-global
|
|
43
|
-
$set NAME = value scope=session
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Scopes: skill-local (persists across fires of this skill, not visible to other skills), agent-global (visible to all skills of the same agent), session (alive for the duration of the current session, cleared at session end). Backed by a configured data-records connector.
|
|
47
|
-
|
|
48
|
-
## Sensors as a read-channel category
|
|
49
|
-
|
|
50
|
-
> `sensor:` is not a trigger source — to fire a skill on a sensor signal, a sensor adapter POSTs to `/event` (see Triggers). The concept below is the separate *read-channel* idea — continuous values an agent reads, distinct from triggering — which remains a future possibility if demand surfaces.
|
|
51
|
-
|
|
52
|
-
Distinct from triggers. Sensors are continuous channels the agent reads but doesn't emit on. Planned syntax:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
# Sensors: presence, screen-state, voice-prosody
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Ambient refs `$(SENSOR.presence)`, `$(SENSOR.voice-prosody.affect)` for read access. Privacy-gating discipline determines when a sensor is readable.
|
|
59
|
-
|
|
60
|
-
## Time as first-class primitives
|
|
61
|
-
|
|
62
|
-
Currently `$(NOW)` (wall-clock). Planned relative-time primitives:
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
$(SECONDS_SINCE_LAST_USER_MESSAGE)
|
|
66
|
-
$(MINUTES_SINCE_SESSION_START)
|
|
67
|
-
$(SECONDS_SINCE_LAST_FIRE_OF.<skill-name>)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Most "right time" reasoning is relative, not wall-clock.
|
|
71
|
-
|
|
72
|
-
## Other planned
|
|
73
|
-
|
|
74
|
-
- **Absence-as-trigger** — `# Triggers: idle: 5m` fire-on-quiet primitive (would be a new trigger source beyond `cron`|`event`, or an adapter that POSTs on a timer)
|
|
75
|
-
- **Time-windowed aggregation** — filter-like primitives across firings (e.g., "user has shown frustration in 3 of 5 recent turns")
|
|
76
|
-
- **Debounce / rate-limit / coalesce** — declarative queueing policy headers
|
|
77
|
-
- **Suppression as valid output** — explicit "fire-and-suppress" (different from `# Output: none`)
|
|
78
|
-
- **Cross-skill pub-sub** — `# Publishes: signal.X` / `# Subscribes: signal.Y` decoupling
|
|
79
|
-
- **Confidence/threshold gating** — `# RequiresConfidence: classifier >= 0.8` / `# RequiresThreshold:`
|
|
80
|
-
- **Invocation-control axis** — `# Invocable-By: user | agent | trigger` (sensitive ops shouldn't leak across invocation boundaries)
|
|
81
|
-
- **Channel/locality awareness** — `$(CHANNEL_TYPE)`, `$(CHANNEL_PRIVACY)` ambient refs for routing decisions
|
|
82
|
-
- **Introspection primitives** — `$(PROMPT_CONTEXT.size)`, `$(SKILLS_FIRED_RECENTLY.last-1h)`, `$(SELF.confidence-trend)`
|
|
83
|
-
- **Capability declarations** — `# Requires-Capabilities: sensors=[mic, camera], tools=[...]` (audit surface for operators)
|
|
84
|
-
|
|
85
|
-
## When the language extends, this section shrinks
|
|
86
|
-
|
|
87
|
-
When any of these primitives ship, the relevant grammar moves into its canonical section (Ops reference, Variables, Triggers, etc.) and the entry here is removed. This section stays alive as a continuous staging area for the next horizon of unshipped work.
|
|
88
|
-
|
|
89
12
|
## Overview & language model
|
|
90
13
|
|
|
91
14
|
Skillscript is a constrained domain-specific language for authoring agent workflows. A skillscript is a declarative recipe: a small program with a dependency DAG of named targets, each composed of typed operations. Skillscripts are written once and executed many times.
|
|
@@ -378,7 +301,7 @@ Closed list of language-intrinsic ops the runtime knows directly. Each is a func
|
|
|
378
301
|
| `emit` | `emit(text="...")` | none | Append to the skill's emission stream; consumed by the configured `# Output:` delivery channel. |
|
|
379
302
|
| `notify` | `notify(agent="...", message="...", [event_type=...], [correlation_id=...]) -> ACK` | optional | Mid-skill agent alert; synchronous send via configured AgentConnector. |
|
|
380
303
|
| `inline` | `inline(skill="<data-skill-name>")` | none | Compile-time inline of an Approved `# Type: data` skill. Resolves at compile, records `content_hash` in provenance. |
|
|
381
|
-
| `execute_skill` | `execute_skill(skill_name="...", inputs
|
|
304
|
+
| `execute_skill` | `execute_skill(skill_name="...", inputs=\{...}) -> R` | optional | Composition primitive. Runtime-resolved. See Composition section. |
|
|
382
305
|
| `shell` | `shell(command="...") -> R` / `shell(argv=[...]) -> R` / `shell(command="...", unsafe=true) -> R` | optional | Structural spawn (default), explicit-argv spawn (`argv=[...]`, no tokenizer), or full-shell exec (`unsafe=true`, gated by `runtime.enable_unsafe_shell`). Binary gated by the operator allowlist (see below). stdout binds. |
|
|
383
306
|
| `file_read` | `file_read(path="...") -> R` | required | Read a file at `path`; binds string contents. |
|
|
384
307
|
| `file_write` | `file_write(path="...", content="...")` | none | Write `content` to `path`. `mkdir -p` semantics for parent directories. Mutation-classified. |
|
|
@@ -735,12 +658,12 @@ deliver:
|
|
|
735
658
|
|
|
736
659
|
| Class | Op | Shape | Binding |
|
|
737
660
|
|---|---|---|---|
|
|
738
|
-
| Mutation | `$set` | `$set NAME = value` (with
|
|
661
|
+
| Mutation | `$set` | `$set NAME = value` (with `$\{VAR}` interpolation at bind) | NAME (no arrow) |
|
|
739
662
|
| Mutation | `$append` | `$append VAR <value>` (type-dispatched: list element / string concat) | VAR (no arrow) |
|
|
740
663
|
| Runtime-intrinsic | `emit` | `emit(text="...")` or `emit(text="""...""")` for multi-line | none |
|
|
741
664
|
| Runtime-intrinsic | `notify` | `notify(agent="...", [message=...], [event_type=...], [correlation_id=...]) -> ACK` | optional |
|
|
742
665
|
| Runtime-intrinsic | `inline` | `inline(skill="<name>")` | none (compile-time) |
|
|
743
|
-
| Runtime-intrinsic | `execute_skill` | `execute_skill(skill_name="...", inputs
|
|
666
|
+
| Runtime-intrinsic | `execute_skill` | `execute_skill(skill_name="...", inputs=\{...}) -> R` | optional |
|
|
744
667
|
| Runtime-intrinsic | `shell` | `shell(command="...", [unsafe=true], [approved="..."]) [-> R] [(fallback: "...")]` or `shell(argv=[...], [approved="..."]) [-> R] [(fallback: "...")]` (mutually exclusive forms; binary allowlist applies to both) | optional |
|
|
745
668
|
| Runtime-intrinsic | `file_read` | `file_read(path="...") -> R` | required |
|
|
746
669
|
| Runtime-intrinsic | `file_write` | `file_write(path="...", content="...", [approved="..."])` | none |
|
|
@@ -769,13 +692,13 @@ Injected automatically at runtime; never declared by the author.
|
|
|
769
692
|
|
|
770
693
|
| Var | Value |
|
|
771
694
|
|-----|-------|
|
|
772
|
-
|
|
|
773
|
-
|
|
|
774
|
-
|
|
|
775
|
-
|
|
|
776
|
-
|
|
|
777
|
-
|
|
|
778
|
-
|
|
|
695
|
+
| `$\{NOW}` | ISO-8601 timestamp at op-dispatch time |
|
|
696
|
+
| `$\{USER}` | The configured user identity |
|
|
697
|
+
| `$\{SESSION_CONTEXT}` | Current session-scope context (project/entity/etc., substrate-defined) |
|
|
698
|
+
| `$\{TRIGGER_TYPE}` | What event fired this skill |
|
|
699
|
+
| `$\{TRIGGER_PAYLOAD}` | Event-specific data |
|
|
700
|
+
| `$\{EVENT.*}` | Event-payload fields populated by the trigger source |
|
|
701
|
+
| `$\{ERROR_CONTEXT}` | In `# OnError:` fallback skills: type + target where failure occurred |
|
|
779
702
|
|
|
780
703
|
Iterator vars from `foreach` and output bindings from runtime-intrinsic / MCP-dispatch ops also pass through ambient at compile time; the runtime substitutes them per iteration / per op completion.
|
|
781
704
|
|
|
@@ -880,14 +803,14 @@ Pipe filters apply transforms to resolved variables before substitution. Syntax:
|
|
|
880
803
|
|
|
881
804
|
| Filter | Effect | Example | Output |
|
|
882
805
|
|--------|--------|---------|--------|
|
|
883
|
-
| `url` | `encodeURIComponent(value)` |
|
|
884
|
-
| `shell` | POSIX single-quote escape with outer quotes |
|
|
885
|
-
| `json` | `JSON.stringify(value)` |
|
|
886
|
-
| `trim` | Whitespace trim |
|
|
887
|
-
| `length` | Count of items (array) or characters (string) |
|
|
888
|
-
| `contains:"X"` | Boolean: type-aware substring / element membership |
|
|
889
|
-
| `fallback:"X"` | Coalesce on missing/undefined/empty ref |
|
|
890
|
-
| `isodate` | Epoch seconds → ISO-8601 timestamp |
|
|
806
|
+
| `url` | `encodeURIComponent(value)` | `$\{location|url}` for `"Asheville, NC"` | `Asheville%2C%20NC` |
|
|
807
|
+
| `shell` | POSIX single-quote escape with outer quotes | `$\{arg|shell}` for `it's safe` | `'it'\''s safe'` |
|
|
808
|
+
| `json` | `JSON.stringify(value)` | `$\{payload|json}` for `\{k:"v"}` | `"\{\"k\":\"v\"}"` |
|
|
809
|
+
| `trim` | Whitespace trim | `$\{VERDICT|trim}` for `"urgent\n"` | `urgent` |
|
|
810
|
+
| `length` | Count of items (array) or characters (string) | `$\{ITEMS|length}` for `["a","b","c"]` | `3` |
|
|
811
|
+
| `contains:"X"` | Boolean: type-aware substring / element membership | `$\{MSG|contains:"urgent"}` for `"Yes, urgent"` | `true` |
|
|
812
|
+
| `fallback:"X"` | Coalesce on missing/undefined/empty ref | `$\{VAR.missing|fallback:"-"}` | `-` |
|
|
813
|
+
| `isodate` | Epoch seconds → ISO-8601 timestamp | `$\{EPOCH|isodate}` for `1779660000` | `2026-05-24T22:00:00.000Z` |
|
|
891
814
|
|
|
892
815
|
### `length` semantics
|
|
893
816
|
|
|
@@ -1501,11 +1424,11 @@ The substrate-routing ops (`$ connector.tool`, `$ data_read`, `$ llm`) and the a
|
|
|
1501
1424
|
|
|
1502
1425
|
## Five connector types
|
|
1503
1426
|
|
|
1504
|
-
###
|
|
1427
|
+
### DataStore
|
|
1505
1428
|
|
|
1506
|
-
Routes `$ data_read` retrieval ops. Interface: `
|
|
1429
|
+
Routes `$ data_read` retrieval ops. Interface: `DataStore.query(filters) → PortableMemory[]`.
|
|
1507
1430
|
|
|
1508
|
-
Implementations vary by deployment — a knowledge-substrate-backed store, a SQLite-backed store, a vector-DB-backed store, an in-memory test store. All conform to the `
|
|
1431
|
+
Implementations vary by deployment — a knowledge-substrate-backed store, a SQLite-backed store, a vector-DB-backed store, an in-memory test store. All conform to the `DataStore.query` contract and return `PortableMemory[]`.
|
|
1509
1432
|
|
|
1510
1433
|
### LocalModel
|
|
1511
1434
|
|
|
@@ -1621,7 +1544,7 @@ Skill records are infrastructure, not knowledge atoms — adopter impls should t
|
|
|
1621
1544
|
|
|
1622
1545
|
All connector types expose `capabilities()` for runtime discovery. Consumers:
|
|
1623
1546
|
1. `# Requires:` matching against the registered set
|
|
1624
|
-
2. Dynamic queries via `
|
|
1547
|
+
2. Dynamic queries via `listDataStores()` / `listLocalModels()` / `listMcpConnectors()` / `listAgentConnectors()` to pick a connector for the moment
|
|
1625
1548
|
3. Authoring tools that surface the registered set
|
|
1626
1549
|
|
|
1627
1550
|
## Multi-instance by design
|
|
@@ -1630,7 +1553,7 @@ Multiple instances of the same connector type are the *normal case*, not the exc
|
|
|
1630
1553
|
|
|
1631
1554
|
```
|
|
1632
1555
|
{
|
|
1633
|
-
primary:
|
|
1556
|
+
primary: DataStoreImplA,
|
|
1634
1557
|
project: SqliteProjectStore,
|
|
1635
1558
|
scratch: InMemoryStore
|
|
1636
1559
|
}
|
|
@@ -1779,7 +1702,7 @@ interface McpDispatchCtx {
|
|
|
1779
1702
|
|
|
1780
1703
|
Hard-coupling skills to specific substrates would make information-flow decisions infrastructural rather than skill-authored, defeating the point of skills as the agent's programming language. The connector layer is what lets the same skill body run against substrate A today and run against substrate B tomorrow without rewriting.
|
|
1781
1704
|
|
|
1782
|
-
## Lifecycle and status — # Status: header,
|
|
1705
|
+
## Lifecycle and status — # Status: header, three canonical states (Draft / Approved / Disabled), compile + runtime enforcement
|
|
1783
1706
|
|
|
1784
1707
|
Skillscripts carry an explicit lifecycle state via the `# Status:` header. The compiler and runtime enforce status — a Disabled skillscript cannot fire under any path, regardless of who invokes it.
|
|
1785
1708
|
|
|
@@ -2037,6 +1960,13 @@ default: fetch
|
|
|
2037
1960
|
|
|
2038
1961
|
**Recursion guard.** The runtime enforces a configurable recursion-depth limit (default 10) to prevent infinite-loop composition. Exceeding the limit raises a clean structured error attributable to the offending dispatch site, not a stack overflow.
|
|
2039
1962
|
|
|
1963
|
+
## Error surfacing in composition
|
|
1964
|
+
|
|
1965
|
+
Two non-obvious behaviors when reading a child's failures from the parent (both confirmed by the v1.0 runtime-semantics test battery):
|
|
1966
|
+
|
|
1967
|
+
- **Nested errors do NOT surface at the top level.** When a child op fails — e.g. the recursion-depth guard fires — the structured error nests inside the child's `R.errors`, which nests inside *its* parent's `R.errors`, and so on up the chain. It does **not** bubble to the caller's top-level `result.errors`. So a top-level `errors: []` does **not** mean nothing failed downstream. To detect a child failure, inspect the bound `${R.errors}` (and deeper), or rely on `(fallback: ...)` / `# OnError:` — those fire on the structured error regardless of nesting depth.
|
|
1968
|
+
- **`# Returns: R` is load-bearing for observability.** If the parent binds a child via `-> R` but does not declare `# Returns: R`, the `# Returns:` filter strips `R` from the parent's `final_vars` — and any nested child errors disappear from the parent's MCP-wire response along with it. A composition skill that wants its child's failures observable from *its own* caller must declare the binding (`# Returns: R`) explicitly. Observability of child failure is opt-in, the same way value export is.
|
|
1969
|
+
|
|
2040
1970
|
## Forward-reference resolution
|
|
2041
1971
|
|
|
2042
1972
|
Skill references (`inline(skill=...)`, `execute_skill(skill_name=...)`) are validated at compile time but allow forward references with tier-2 advisories — making it possible to author sibling skills together (chicken-and-egg).
|
|
@@ -2152,6 +2082,7 @@ For *data skills* (skills marked `# Type: data`), the compile-time inline primit
|
|
|
2152
2082
|
|
|
2153
2083
|
- Treat composition as a real cost. Each `execute_skill()` dispatch incurs the child's full execution time + side effects. Don't compose for trivial cases that could be inlined.
|
|
2154
2084
|
- Declare `# Returns:` when a caller needs structured access to a child's variables. Leave it off for emit-only skills whose consumers read `.outputs.text` — the default-empty filter keeps scratch from propagating.
|
|
2085
|
+
- Want a child's failures visible to your caller? Declare the child binding in `# Returns:` — nested errors are filtered out with the binding otherwise (see Error surfacing in composition), and don't trust a top-level `errors: []`.
|
|
2155
2086
|
- Pair composition with `(fallback: ...)` when the child skill might fail and the parent has a sensible degraded path.
|
|
2156
2087
|
- Use mechanical mode to TestFlight any multi-skill chain before shipping it as a Headless skill on a cron trigger.
|
|
2157
2088
|
- Forward references work — author sibling skills in any order, validate independently. The tier-2 warning surfaces the deferred-resolution path; runtime catches genuine misses.
|
|
@@ -2481,50 +2412,72 @@ Some features depend on others:
|
|
|
2481
2412
|
|
|
2482
2413
|
The "Not yet implemented, but planned" section at top tracks the user-facing surface; this section preserves the design-order argument for when implementation work picks up.
|
|
2483
2414
|
|
|
2484
|
-
##
|
|
2415
|
+
## Not yet implemented, but planned
|
|
2416
|
+
|
|
2417
|
+
These are features designed or anticipated but not yet implemented in the current build. Authors should not use these forms; they will not compile.
|
|
2418
|
+
|
|
2419
|
+
## Control flow
|
|
2485
2420
|
|
|
2486
|
-
|
|
2421
|
+
- **`while CONDITION:` loops** — today's iteration is `foreach IDENT in EXPR:` only. While loops are planned for ad-hoc orchestration patterns ("loop until response contains 'done'").
|
|
2422
|
+
- **Arithmetic in `$set`** — today accepts literals + `${VAR}` interpolation; no `+ - * /` operators. Planned alongside `while` for turn counters and orchestration bookkeeping.
|
|
2487
2423
|
|
|
2488
|
-
##
|
|
2424
|
+
## Pipe filter extensions
|
|
2425
|
+
|
|
2426
|
+
- **Array aggregation primitives** — `|max`, `|min`, `|sum`, `|reduce` over arrays. Today the language tops out at "shape one record" — aggregating across an array requires `foreach` accumulator ceremony. Planned as a design question: is `foreach` the deliberate ceiling for aggregation, or do we add primitives?
|
|
2427
|
+
|
|
2428
|
+
## Triggers — note
|
|
2429
|
+
|
|
2430
|
+
Trigger sources are `cron` + `event` only (see the Triggers section). There is no separate `agent-event:` / `file-watch:` / `sensor:` trigger source — anything that isn't time-based is an external adapter that POSTs to the `/event` ingress. So there's nothing pending here on the trigger axis; the entry remains only to point readers who expect those sources at the adapter-POST pattern.
|
|
2489
2431
|
|
|
2490
|
-
Within a target body, op ordering and variable binding conventions aren't fully written down. Specific questions:
|
|
2491
|
-
- Can `emit()` calls precede `$` ops in the same target? (Yes; `emit()` has no dependency on subsequent ops.)
|
|
2492
|
-
- What's the default output binding when `-> NAME` is omitted? (`${target.output}` — same as bare `target` referenced from other blocks.)
|
|
2493
|
-
- How do cross-block references work syntactically? (`${other_target.output}` or `${VAR_BOUND_THERE}`.)
|
|
2494
2432
|
|
|
2495
|
-
|
|
2433
|
+
## Tests
|
|
2496
2434
|
|
|
2497
|
-
|
|
2435
|
+
- **`# Tests:` block** with `given:` / `expect:` assertions — author-authored test cases. Will land when adopter signal demands test infrastructure.
|
|
2498
2436
|
|
|
2499
|
-
|
|
2437
|
+
## Output kinds
|
|
2500
2438
|
|
|
2501
|
-
|
|
2439
|
+
- **`# Output: file: <path>`** — file-output routing parses but no router exists today.
|
|
2440
|
+
- **`# Output: card:`** — depends on a substrate-side card render surface; not implemented.
|
|
2502
2441
|
|
|
2503
|
-
|
|
2442
|
+
## Persistent state with declared scope
|
|
2504
2443
|
|
|
2505
|
-
|
|
2444
|
+
```
|
|
2445
|
+
$set NAME = value scope=skill-local
|
|
2446
|
+
$set NAME = value scope=agent-global
|
|
2447
|
+
$set NAME = value scope=session
|
|
2448
|
+
```
|
|
2506
2449
|
|
|
2507
|
-
|
|
2450
|
+
Scopes: skill-local (persists across fires of this skill, not visible to other skills), agent-global (visible to all skills of the same agent), session (alive for the duration of the current session, cleared at session end). Backed by a configured data-records connector.
|
|
2508
2451
|
|
|
2509
|
-
## 5. Output target delivery failures
|
|
2510
2452
|
|
|
2511
|
-
|
|
2453
|
+
## Time as first-class primitives
|
|
2512
2454
|
|
|
2513
|
-
|
|
2455
|
+
Currently `$(NOW)` (wall-clock). Planned relative-time primitives:
|
|
2514
2456
|
|
|
2515
|
-
|
|
2457
|
+
```
|
|
2458
|
+
$(SECONDS_SINCE_LAST_USER_MESSAGE)
|
|
2459
|
+
$(MINUTES_SINCE_SESSION_START)
|
|
2460
|
+
$(SECONDS_SINCE_LAST_FIRE_OF.<skill-name>)
|
|
2461
|
+
```
|
|
2516
2462
|
|
|
2517
|
-
|
|
2463
|
+
Most "right time" reasoning is relative, not wall-clock.
|
|
2518
2464
|
|
|
2519
|
-
|
|
2465
|
+
## Other planned
|
|
2520
2466
|
|
|
2521
|
-
|
|
2467
|
+
- **Absence-as-trigger** — `# Triggers: idle: 5m` fire-on-quiet primitive (would be a new trigger source beyond `cron`|`event`, or an adapter that POSTs on a timer)
|
|
2468
|
+
- **Time-windowed aggregation** — filter-like primitives across firings (e.g., "user has shown frustration in 3 of 5 recent turns")
|
|
2469
|
+
- **Debounce / rate-limit / coalesce** — declarative queueing policy headers
|
|
2470
|
+
- **Suppression as valid output** — explicit "fire-and-suppress" (different from `# Output: none`)
|
|
2471
|
+
- **Cross-skill pub-sub** — `# Publishes: signal.X` / `# Subscribes: signal.Y` decoupling
|
|
2472
|
+
- **Confidence/threshold gating** — `# RequiresConfidence: classifier >= 0.8` / `# RequiresThreshold:`
|
|
2473
|
+
- **Invocation-control axis** — `# Invocable-By: user | agent | trigger` (sensitive ops shouldn't leak across invocation boundaries)
|
|
2474
|
+
- **Introspection primitives** — `$(PROMPT_CONTEXT.size)`, `$(SKILLS_FIRED_RECENTLY.last-1h)`, `$(SELF.confidence-trend)`
|
|
2522
2475
|
|
|
2523
|
-
|
|
2476
|
+
## When the language extends, this section shrinks
|
|
2524
2477
|
|
|
2525
|
-
|
|
2478
|
+
When any of these primitives ship, the relevant grammar moves into its canonical section (Ops reference, Variables, Triggers, etc.) and the entry here is removed. This section stays alive as a continuous staging area for the next horizon of unshipped work.
|
|
2526
2479
|
|
|
2527
2480
|
---
|
|
2528
2481
|
|
|
2529
|
-
*Rendered from `skillscript/skillscript-language-reference` — 2026-06-15
|
|
2482
|
+
*Rendered from `skillscript/skillscript-language-reference` — 2026-06-16 15:46 EDT*
|
|
2530
2483
|
*Source of truth: AMP (`amp_render_document("skillscript/skillscript-language-reference")`)*
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: SqliteSkillStore
|
|
3
|
+
description: "Worked example of SkillStore against a SQLite database — schema, semantics, forking checklist."
|
|
4
|
+
mode: wide
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
Worked example of `SkillStore` against a SQLite database. Copy this directory into your codebase, customize per your storage needs, register with skillscript-runtime's `Registry`. This README is written for the agent implementing your adopter's connector — including the human reviewing the PR.
|
|
4
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: classify-support-ticket
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
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
|
|
5
5
|
# Vars: TICKET_TEXT, TICKET_ID
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: data-store-roundtrip
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
4
|
# Description: Round-trips the DataStore — writes a record tagged with a per-run marker in the content, reads it back via FTS query against the same marker. The exact item count depends on substrate FTS matching strictness (strict FTS substrates return 1; substrates with looser token-match semantics may return prior runs' records that share token shapes). If this skill executes and the output line shows N ≥ 1 items returned, your DataStore substrate is wired correctly. For adopters needing deterministic counts (e.g. authoring round-trip tests), the `domain_tags` filter is the portable strict-match read path — see the adopter-playbook §"Notable things..." for the pattern.
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: feedback-sentiment-scan
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: Each night, scan the previous 24h of customer feedback records, classify sentiment via the local model, and surface entries that read 'frustrated' or 'blocking' so the team sees them at start-of-day.
|
|
4
4
|
# Triggers: cron: 0 3 * * *
|
|
5
5
|
# Vars: SCAN_LIMIT=50
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: hello-world
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: The canonical first-run example. No substrate dependencies, no compute block — pure declarative output. If this fails to execute, your install or runtime is broken; investigate before troubleshooting deeper layers. Demonstrates the template-only shape: body text IS the skill; `# Vars:` declared inputs feed the template with `--input` override.
|
|
4
4
|
# Vars: WHO=world
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: morning-brief
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: Compose a daily morning brief from calendar, mailbox, and overnight notes 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. Requires a `calendar` connector configured in connectors.json (the dotted `$ calendar.*` form). `model=qwen` is a representative LocalModel alias — adopters register one under whatever name fits (the bundled bootstrap registers `default`).
|
|
4
4
|
# Vars: AGENT, BRIEF_HORIZON_HOURS=24
|
|
5
5
|
# Triggers: cron: 0 7 * * *
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: queue-length-monitor
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: Count pending items in a queue and alert when the count exceeds threshold. Requires `cat` on the operator's shell allowlist (default-deny: a non-allowlisted binary is refused).
|
|
4
4
|
# Vars: QUEUE_PATH=/var/queue/pending.json, THRESHOLD=10
|
|
5
5
|
# Triggers: cron: */5 * * * *
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: service-health-watch
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
4
|
# Description: Every 5 minutes check named service endpoints — if latency or status degrades, write a signal record and alert. Requires `curl` on the operator's shell allowlist (default-deny: a non-allowlisted binary is refused).
|
|
5
5
|
# Vars: SERVICES=[auth-api, ledger-api, search-api], LATENCY_BUDGET_MS=400
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: skill-store-roundtrip
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
4
|
# Description: Round-trips the SkillStore — writes a child skill, reads it back. Demonstrates the Lisp-shape primitive (skills can write skills). NOTE: 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
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: youtrack-morning-sweep
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: Pull recent YouTrack issues via a configured RemoteMcpConnector + dotted field access on parsed JSON + foreach over the issues array. Requires a `youtrack` connector configured in connectors.json (RemoteMcpConnector class, mcp-remote bridge, newline framing) with allowed_tools including search_issues + get_current_user.
|
|
4
4
|
|
|
5
5
|
fetch_me:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillscript-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Runtime, compiler, lint, CLI, and dashboard for Skillscript — a small declarative language for authoring agent workflows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Scott Shwarts <scotts@pobox.com>",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: data-store-roundtrip
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
4
|
# Description: Round-trips the DataStore — writes a record tagged with a per-run marker in the content, reads it back via FTS query against the same marker. The exact item count depends on substrate FTS matching strictness (strict FTS substrates return 1; substrates with looser token-match semantics may return prior runs' records that share token shapes). If this skill executes and the output line shows N ≥ 1 items returned, your DataStore substrate is wired correctly. For adopters needing deterministic counts (e.g. authoring round-trip tests), the `domain_tags` filter is the portable strict-match read path — see the adopter-playbook §"Notable things..." for the pattern.
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: hello-world
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Description: The canonical first-run example. No substrate dependencies, no compute block — pure declarative output. If this fails to execute, your install or runtime is broken; investigate before troubleshooting deeper layers. Demonstrates the template-only shape: body text IS the skill; `# Vars:` declared inputs feed the template with `--input` override.
|
|
4
4
|
# Vars: WHO=world
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Skill: skill-store-roundtrip
|
|
2
|
-
# Status:
|
|
2
|
+
# Status: Draft
|
|
3
3
|
# Autonomous: true
|
|
4
4
|
# Description: Round-trips the SkillStore — writes a child skill, reads it back. Demonstrates the Lisp-shape primitive (skills can write skills). NOTE: 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
5
|
|