skillscript-runtime 0.33.0 → 0.35.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/CHANGELOG.md +28 -0
- package/README.md +5 -4
- package/dist/bootstrap-from-env.d.ts.map +1 -1
- package/dist/bootstrap-from-env.js +2 -0
- package/dist/bootstrap-from-env.js.map +1 -1
- package/dist/bootstrap.d.ts +9 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +4 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/composition.d.ts +4 -0
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +12 -0
- package/dist/composition.js.map +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +11 -0
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/types.d.ts +36 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js.map +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +60 -0
- package/dist/errors.js.map +1 -1
- package/dist/help-content.d.ts +1 -1
- package/dist/help-content.d.ts.map +1 -1
- package/dist/help-content.js +5 -1
- package/dist/help-content.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +38 -0
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts +8 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +8 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/parser.d.ts +11 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +17 -0
- package/dist/parser.js.map +1 -1
- package/dist/runtime-config.d.ts +8 -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 +2 -0
- package/dist/runtime-env-resolver.d.ts.map +1 -1
- package/dist/runtime-env-resolver.js +8 -0
- package/dist/runtime-env-resolver.js.map +1 -1
- package/dist/runtime.d.ts +86 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +325 -76
- package/dist/runtime.js.map +1 -1
- package/dist/scheduler.d.ts +3 -0
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +3 -0
- package/dist/scheduler.js.map +1 -1
- package/dist/trace.d.ts +22 -2
- package/dist/trace.d.ts.map +1 -1
- package/dist/trace.js +7 -1
- package/dist/trace.js.map +1 -1
- package/docs/adopter-playbook.md +31 -0
- package/docs/connector-contract-reference.md +38 -0
- package/docs/language-reference.md +95 -46
- package/examples/connectors/README.md +2 -2
- package/examples/connectors/RestConnector/.env.example +19 -0
- package/examples/connectors/RestConnector/README.md +109 -0
- package/examples/connectors/RestConnector/RestConnector.ts +326 -0
- package/examples/skillscripts/classify-support-ticket.skill.provenance.json +1 -1
- package/examples/skillscripts/data-store-roundtrip.skill.provenance.json +1 -1
- package/examples/skillscripts/doc-qa-with-citations.skill.provenance.json +1 -1
- package/examples/skillscripts/feedback-sentiment-scan.skill.provenance.json +1 -1
- package/examples/skillscripts/hello-world.skill.provenance.json +1 -1
- package/examples/skillscripts/morning-brief.skill.provenance.json +1 -1
- package/examples/skillscripts/queue-length-monitor.skill.provenance.json +1 -1
- package/examples/skillscripts/service-health-watch.skill.provenance.json +1 -1
- package/examples/skillscripts/skill-store-roundtrip.skill.provenance.json +1 -1
- package/examples/skillscripts/youtrack-morning-sweep.skill.provenance.json +1 -1
- package/package.json +1 -1
- package/scaffold/.env.example +9 -0
package/dist/trace.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAQ,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AA+B/C,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"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"}
|
package/docs/adopter-playbook.md
CHANGED
|
@@ -52,8 +52,12 @@ AgentConnector — substrate-neutral agent delivery
|
|
|
52
52
|
|
|
53
53
|
McpConnector — external tool dispatch (substrate-neutral wire)
|
|
54
54
|
call(toolName, args, ctx?) "invoke this tool with these kwargs"
|
|
55
|
+
onAbort(budgetMs)? "deadline cut an in-flight call — stop your effect within this budget"
|
|
56
|
+
(only for effectBoundary: "outlives-call" connectors)
|
|
55
57
|
```
|
|
56
58
|
|
|
59
|
+
`ctx.signal` on `call(...)` is an `AbortSignal` the runtime aborts when a deadline (or per-op timeout) cuts the call — forward it to your fetch/RPC to truly cancel. If your effect *outlives* the call (a robot keeps moving after `call()` returns), declare `effectBoundary: "outlives-call"` and implement `onAbort(budgetMs)` for a bounded safety-stop; the runtime refuses to register such a connector without it. Full behavior: [Bounding runs — deadlines & cancellation](#bounding-runs--deadlines--cancellation).
|
|
60
|
+
|
|
57
61
|
What's NOT in the contracts (and is your concern as implementor):
|
|
58
62
|
- Where the data lives (sqlite / your DB / hosted service / vector store)
|
|
59
63
|
- What metadata fields your substrate honors or ignores (kwargs passed via `metadata.<key>` ride through; you choose what to do with them)
|
|
@@ -214,6 +218,7 @@ Security knobs that adopters wiring real substrates should know about:
|
|
|
214
218
|
|
|
215
219
|
- **The approval boundary (secured mode).** `SKILLSCRIPT_SECURED_MODE=true` enforces that only key-signed skills perform effects. Default-deny by design — leave it off only for trusted-author / single-operator setups. Full detail, the approve flow, and key custody: [Approval + secured mode](#approval--secured-mode).
|
|
216
220
|
- **Filesystem path allowlist.** `SKILLSCRIPT_FS_ALLOWLIST` is default-deny — `file_read` / `file_write` refuse every path until you wire roots. See [Filesystem path allowlist](#filesystem-path-allowlist). (Keep your approval-key directory out of it.)
|
|
221
|
+
- **Run deadline ceiling.** `SKILLSCRIPT_MAX_DEADLINE_SECONDS` caps the wall-clock time of *every* run — enforced even for a skill that declares no `# Deadline:`, so an agent author can't evade it by omission. Unset = no ceiling. See [Bounding runs — deadlines & cancellation](#bounding-runs--deadlines--cancellation). Set a small value on a robotic / latency-sensitive host.
|
|
217
222
|
- **Per-connector tool allowlists** — `allowed_tools` on each `connectors.json` MCP connector entry restricts which tools that connector can dispatch. Three-state (`undefined` = allow all, `[]` = allow none, listed = exactly those). Tier-1 `disallowed-tool` lint + runtime defense-in-depth refuse out-of-list dispatch. **`allowed_tools` belongs at the entry top-level**, sibling to `class` and `config` — NOT inside the `config` block. The loader hard-errors on misplacement (placing it inside `config:` would silently allow-all every tool — the worst-case failure mode for a security control). See `docs/configuration.md` §"Named MCP connector instances" for the JSON shape.
|
|
218
223
|
- **Shell-execution discipline** — `shell(command="...")` runs structured-spawn by default (binary on PATH, whitespace-tokenized argv, no bash). `shell(command="...", unsafe=true)` opts into bash interpretation (pipes, `$VAR`, command substitution) and refuses to fire unless the runtime is configured with `enable_unsafe_shell = true` in `config.toml`. Lint flags every `unsafe=true` op tier-2 to keep audit posture visible. See `scaffold/config.toml` for the documented default + `help({topic:"lint-codes"})` for the `unsafe-shell-disabled` rule.
|
|
219
224
|
|
|
@@ -631,6 +636,21 @@ SKILLSCRIPT_FS_ALLOWLIST=/srv/skillscript/workspace,/var/skillscript/events
|
|
|
631
636
|
|
|
632
637
|
TOCTOU note: the check resolves the real path at call time; a symlink swapped between check and open is a residual closed by fd-based opens later. Checking the resolved real path is the standard mitigation shipped today.
|
|
633
638
|
|
|
639
|
+
## Bounding runs — deadlines & cancellation
|
|
640
|
+
|
|
641
|
+
**A skill can declare a `# Deadline: N` (seconds) — a wall-clock budget for the whole run — and the operator can impose a hard ceiling on every run.** This is the time-bounding boundary, alongside the shell / fs / secret boundaries above. It matters most for autonomous fires and for connectors whose effects touch the physical world (a robot, a long external job): "timeout" should mean both *bounded* and *actually stopped*, and here it does.
|
|
642
|
+
|
|
643
|
+
| Operator switch | Controls |
|
|
644
|
+
|---|---|
|
|
645
|
+
| `SKILLSCRIPT_MAX_DEADLINE_SECONDS` | A hard maximum on **every** run, enforced even when a skill declares no `# Deadline:`. A skill's own `# Deadline:` may only make the bound *tighter*, never exceed it. Applies on both the server/MCP path and the `skillfile execute` CLI. Unset = no ceiling. |
|
|
646
|
+
|
|
647
|
+
- **The deadline propagates and can only tighten.** Set once at the root and shared (by *remaining* time) with everything the run composes — a deep `$ execute_skill` gather cannot outlive the root's bound. A child's own `# Deadline:` tightens further; nothing loosens it.
|
|
648
|
+
- **The ceiling is the guard against an untrusted author.** Skills are written by agents. A skill that simply *omits* `# Deadline:` would otherwise run unbounded — so the operator ceiling bounds it regardless. On a latency-sensitive or robotic host, set a small ceiling so no run (or motion) can hang.
|
|
649
|
+
- **Exceeding the deadline is uncatchable.** It terminates the run via `RunDeadlineExceeded`, which op `(fallback:)` trailers and target `else:` blocks do **not** catch — a fallback-laden skill can't cascade to a looks-complete result past the bound. The run returns a **partial** result flagged `deadline_exceeded`.
|
|
650
|
+
- **Cancellation is real, and connectors participate.** A `shell(...)` child is killed by process-group SIGKILL. A `$ connector.tool` dispatch receives an `AbortSignal` on its dispatch `ctx.signal` — forward it to your fetch/RPC and the work genuinely stops (ignore it and it degrades to a race-and-abandon, today's behavior). **If your connector's effect OUTLIVES the call** (a robot keeps moving after `call()` returns), declare `effectBoundary: "outlives-call"` and an `onAbort(budgetMs)` hook: on an in-flight cut the runtime aborts the call, then gives `onAbort` a bounded reserve slice to issue a real safety-stop (halt motion), and the run still returns within the deadline. The registry **refuses to register** an outlives-call connector without `onAbort` — a leak you'd otherwise only find in production.
|
|
651
|
+
- **A cut mid-mutation is logged as uncertain, not failed.** Aborting stops the client, but the request may already have reached the backend — so the runtime records it as `uncertain_effects: [{ ..., reason: "issued, outcome uncertain" }]` rather than claiming success or failure. It surfaces on the `execute_skill` return **and** the durable trace record (`deadline_exceeded` + `uncertain_effects`) — so an autonomous cron/event fire, which has no live caller, still leaves the "a mutation may have landed" signal in the record for an operator to reconcile. Reads are excluded.
|
|
652
|
+
- **Editing a `# Deadline:` re-approves.** The value is in the approval signing hash (unlike `# Tags:`), so changing it — even tightening — invalidates the signature and drops the skill to Draft. The deadline is the safety envelope the approver signed off on.
|
|
653
|
+
|
|
634
654
|
## Secrets
|
|
635
655
|
|
|
636
656
|
**A skill can reference an operator-provisioned secret by name, use it at a sink, and never read it back.** This is how a credential (a bearer token, an API key) reaches a `shell(...)` or `$ connector.tool` call without living in the skill source, the transcript, or a trace.
|
|
@@ -670,6 +690,17 @@ SKILLSCRIPT_SECRET_AGENTMAIL_KEY=am_live_...
|
|
|
670
690
|
|
|
671
691
|
(On the shell path the resolved value is also briefly visible in the host process list via `ps`; a future vault-backed `SecretProvider` closes that with sink-aware injection. The `SecretProvider` seam is already in place — an adopter can supply a vault-backed provider via `bootstrap({ secretProvider })` or `bootstrapFromEnv`'s `overrides` with no skill changes.)
|
|
672
692
|
|
|
693
|
+
### Credential-free egress — the outbound-proxy pattern
|
|
694
|
+
|
|
695
|
+
The strongest way to keep a credential out of a skill is to keep it out of the **runtime** entirely: front all outbound traffic with a proxy or gateway that injects auth, and give skillscript no token at all.
|
|
696
|
+
|
|
697
|
+
- A `shell(...)` op spawns its child with the runtime's process environment, **minus every `SKILLSCRIPT_SECRET_*` var** (those are scrubbed — see below). Everything else the runtime holds — `HTTPS_PROXY`, `NO_PROXY`, a trusted-CA path like `NODE_EXTRA_CA_CERTS`, `CURL_CA_BUNDLE`, `AWS_*` for an IAM-authenticating sidecar — is inherited by `curl` and every other shell child. Set the proxy variables once on the runtime host and every shell egress routes through the gateway automatically.
|
|
698
|
+
- A `$ connector.tool` egress does the same by pointing the connector's `baseUrl` at the gateway. The [`RestConnector` worked example](../examples/connectors/RestConnector/) shows this: leave the auth token unset and target the proxy — the gateway attaches the real credential on the way out.
|
|
699
|
+
|
|
700
|
+
The gateway holds the credential and injects it per-request; the skill, the transcript, the trace, and the runtime env all stay clean, and rotation happens in one place instead of across every `.env`. This is the deployment-side complement to `{{secret.NAME}}` (which is the right tool when the runtime *must* hold the credential): prefer the proxy when your environment can inject auth at the egress boundary, and reach for a named secret when it can't.
|
|
701
|
+
|
|
702
|
+
**Secret vars are scrubbed from shell children — the selective part of "no scrubbing."** The egress variables above inherit freely; the `SKILLSCRIPT_SECRET_*` namespace does **not**. The runtime resolves a `{{secret.NAME}}` marker itself and splices the value straight into the spawn argv at the sink, so a shell child never legitimately needs the raw secret var in its ambient env — the only thing that would read it there is an *undeclared* ambient read, the exact pattern `# Requires` exists to gate. Scrubbing the prefix (on both the structured and `unsafe=true` paths) makes `# Requires` least-privilege **authoritative** rather than advisory: a shell op sees a provisioned secret only by *declaring* it and placing a `{{secret.NAME}}` marker, never by reading `$SKILLSCRIPT_SECRET_NAME` out of the ambient environment. This is why the scrub is secret-vars-only and not a blanket env wipe — the proxy pattern depends on the egress vars still inheriting.
|
|
703
|
+
|
|
673
704
|
## Trigger model
|
|
674
705
|
|
|
675
706
|
**The trigger surface is two primitives.**
|
|
@@ -250,6 +250,44 @@ These are the load-bearing semantic rules. Internalize before implementing.
|
|
|
250
250
|
|
|
251
251
|
---
|
|
252
252
|
|
|
253
|
+
## McpConnector — the dispatch surface (wire-protocol-agnostic)
|
|
254
|
+
|
|
255
|
+
`McpConnector` is the contract behind every `$ <connector>.<tool>` op. It is the narrowest of the five contracts — two required methods:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
interface McpConnector {
|
|
259
|
+
call(toolName: string, args: Record<string, unknown>, ctx?: McpDispatchCtx): Promise<unknown>;
|
|
260
|
+
manifest(): Promise<ManifestInfo>;
|
|
261
|
+
describeTools?(): Promise<McpToolDescriptor[]>; // optional — author-time discovery + input lint
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**The name does not mean the backend must speak MCP.** "MCP" names the *skill-facing dispatch verb* (`$ connector.tool`), not a wire-protocol requirement. The MCP JSON-RPC framing is `HttpMcpConnector`'s implementation detail because it fronts MCP servers — it is not part of the contract. Any backend satisfies the contract as long as `call()` maps a tool name + args to a result and `manifest()` reports metadata:
|
|
266
|
+
|
|
267
|
+
| Connector | Backend wire protocol |
|
|
268
|
+
|---|---|
|
|
269
|
+
| `HttpMcpConnector` (bundled) | JSON-RPC-over-HTTP (fronts MCP servers) |
|
|
270
|
+
| `RemoteMcpConnector` (bundled) | stdio-bridged MCP (spawned child process) |
|
|
271
|
+
| `RestConnector` ([worked example](../examples/connectors/RestConnector/)) | plain REST/HTTP — **no MCP wire protocol** |
|
|
272
|
+
| your fork | WebSocket, gRPC, in-process, custom — anything |
|
|
273
|
+
|
|
274
|
+
To wrap a REST/HTTP backend, you do **not** need an MCP server in front of it. Implement `call()` to issue the HTTP request directly. The [`examples/connectors/RestConnector/`](../examples/connectors/RestConnector/) worked example is a complete, typechecked reference: an `ENDPOINTS` table maps each tool to an HTTP method + path, `call()` templates the path + routes args to query-string or JSON body + injects the auth header, `staticTools()` returns the closed tool set so lint validates `$ name.tool` at authoring time, and `describeTools()` surfaces the endpoints for discovery.
|
|
275
|
+
|
|
276
|
+
**Heterogeneous connectors coexist.** The registry holds a mixed set keyed by name; each `$ <name>.<tool>` op routes to whichever connector owns that name. A single skill body can freely mix an MCP connector and a REST one — a skill can't tell them apart:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
$ gmail.send to="ops@acme.io" subject="Deploy done" # HttpMcpConnector / RemoteMcpConnector
|
|
280
|
+
-> _
|
|
281
|
+
$ tickets.create title="Deploy 4.2" severity="info" # RestConnector (plain REST)
|
|
282
|
+
-> ticket
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**What the descriptor does *not* carry.** `McpToolDescriptor` is `{ name, description?, inputSchema? }` — there is no `mutating` flag. Read-vs-write intent, when it matters, rides in the description text + the operation semantics, not a connector-descriptor field; a skill's declared effect footprint is the author-side surface for that concern.
|
|
286
|
+
|
|
287
|
+
**Identity propagation** is opt-in via `staticCapabilities().features.supports_identity_propagation`. When false (the default, and what a plain REST wrapper does), `ctx.agentId` is ignored. Flipping it true is a structural claim that distinct `ctx.agentId` values reach the backend AND yield distinct observable substrate scopes — which obligates the Level 1 / Level 2 conformance probes (see `examples/connectors/McpConnectorTemplate/`).
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
253
291
|
## Storage-layer conventions (SkillStore + DataStore)
|
|
254
292
|
|
|
255
293
|
The following conventions 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.
|
|
@@ -6,7 +6,7 @@ mode: wide
|
|
|
6
6
|
|
|
7
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.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
It describes the current runtime; release history lives in the CHANGELOG. Items not yet implemented are called out in the relevant sections.
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
## Overview & language model
|
|
@@ -29,11 +29,23 @@ Skillscript's job is to express this pipeline declaratively. When there is an ag
|
|
|
29
29
|
|
|
30
30
|
## Two execution paths
|
|
31
31
|
|
|
32
|
-
**
|
|
32
|
+
The two paths differ by **who executes the ops** — the runtime, or an agent — *not* by who invokes the skill. Getting this backwards is the classic trap: an agent calling a skill mid-conversation still gets deterministic runtime execution. Determinism is a property of the execution path, not of the caller's context.
|
|
33
33
|
|
|
34
|
-
**
|
|
34
|
+
**Runtime-mediated** — the interpreter walks the ops and dispatches them through configured connectors, returning a completed, deterministic result. This is *every actual execution*, regardless of caller: autonomous cron/event fires, the CLI execute command, `execute_skill` over MCP (an agent invoking a stored skill mid-conversation — the common case), in-skill `$ execute_skill` composition, and `inline(skill=...)`. If a skill *runs*, it runs here. Safety boundary is the connector config + per-op gating (see Ops Reference).
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
**Agent-mediated** — *not* an execution path in the "runs the ops" sense. It's the **compile/preview** path: `compile_skill` renders the skill as a prompt (no side effects, nothing executed), and an agent may then read that prompt and carry out the steps through its *own* tools. Determinism is not guaranteed here, because the runtime never touches the ops. Safety boundary is the agent's harness tool permissions.
|
|
37
|
+
|
|
38
|
+
**Neither of those is `# Output: agent:`.** That's a *delivery* target — a runtime-executed (deterministic) skill handing its rendered output to an agent for its next turn. "Agent" there names who *receives* the result, not who executes the ops.
|
|
39
|
+
|
|
40
|
+
### Which call gets which path
|
|
41
|
+
|
|
42
|
+
| Call | Path | Result |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `execute_skill({name})` (MCP), CLI execute, cron/event fire, `$ execute_skill`, `inline(skill=…)` | Runtime-mediated | The runtime walks the ops and returns the finished result. **Deterministic.** |
|
|
45
|
+
| `compile_skill({name})` | Agent-mediated | Returns the rendered plan/prompt for inspection. No side effects, nothing executed. |
|
|
46
|
+
| `# Output: agent: <name>` | Runtime-mediated + push delivery | A runtime-executed skill pushes its rendered output to an agent. "Agent" is the delivery target, not the executor. |
|
|
47
|
+
|
|
48
|
+
The language is identical across paths. Which path applies is a deployment-time + invocation-time decision — but **execution determinism is a property of runtime dispatch (`execute_skill` and friends), never of the caller's context.** That is the guarantee the runtime makes, and the reason `execute_skill` mid-conversation is a deterministic replacement for ad-hoc agent tool-sequencing.
|
|
37
49
|
|
|
38
50
|
## Output production and delivery channels
|
|
39
51
|
|
|
@@ -301,9 +313,9 @@ Closed list of language-intrinsic ops the runtime knows directly. Each is a func
|
|
|
301
313
|
| `emit` | `emit(text="...")` | none | Append to the skill's emission stream; consumed by the configured `# Output:` delivery channel. |
|
|
302
314
|
| `notify` | `notify(agent="...", message="...", [event_type=...], [correlation_id=...]) -> ACK` | optional | Mid-skill agent alert; synchronous send via configured AgentConnector. |
|
|
303
315
|
| `inline` | `inline(skill="<data-skill-name>")` | none | Compile-time inline of an Approved `# Type: data` skill. Resolves at compile, records `content_hash` in provenance. |
|
|
304
|
-
| `execute_skill` | `execute_skill(name="...", inputs
|
|
316
|
+
| `execute_skill` | `execute_skill(name="...", inputs={...}) -> R` | optional | Composition primitive. Runtime-resolved. `skill_name=` accepted as back-compat alias. See Composition section. |
|
|
305
317
|
| `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. |
|
|
306
|
-
| `file_read` | `file_read(path="...") -> R` | required | Read a file at `path`; binds string contents. Optional `encoding="utf8"
|
|
318
|
+
| `file_read` | `file_read(path="...") -> R` | required | Read a file at `path`; binds string contents. Optional `encoding="utf8"|"base64"` kwarg (default `utf8`). |
|
|
307
319
|
| `file_write` | `file_write(path="...", content="...")` | none | Write `content` to `path`. `mkdir -p` semantics for parent directories. Mutation-classified. |
|
|
308
320
|
|
|
309
321
|
**Unknown op name** → tier-1 lint `unknown-runtime-op` with remediation pointing at MCP dispatch: "if this is an external tool, use `$ <connector>.<tool> args -> R`."
|
|
@@ -517,7 +529,7 @@ $ youtrack.run_report report_id="INFRA-weekly" timeout=120 -> DISPATCH
|
|
|
517
529
|
$ data_write content="${REPORT}" tags=["oncall"] approved="morning roundup" -> ACK
|
|
518
530
|
```
|
|
519
531
|
|
|
520
|
-
**`(fallback: "value")` trailer** — Uniform
|
|
532
|
+
**`(fallback: "value")` trailer** — Uniform: fires on ANY op failure — a dispatch throw (including a `timeout=N` expiry), an empty bound value (empty string after trim, empty array, null/undefined), OR a raised throw from an `execute_skill` child or a `$ json_parse` off-shape input. Honored on `$` dispatch and on `shell()` (fires on shell op throw or empty stdout). Coerce-on-bind: the fallback value binds to the output var transparently, downstream targets need no conditional. Permissive value parsing — bare identifiers, quoted strings, bracketed array literals all accepted. A fired fallback is recorded in `result.fallbacks[]` (with its `.reason`).
|
|
521
533
|
|
|
522
534
|
Note: an envelope object like `{items: []}` is a non-empty object and does NOT trigger the fallback even though its contained array is empty. To handle envelope-empty downstream, test the contained collection (`if ${R.items|length} == "0":`) or apply a filter (`${R.items|fallback:[]}`).
|
|
523
535
|
|
|
@@ -664,14 +676,14 @@ deliver:
|
|
|
664
676
|
|
|
665
677
|
| Class | Op | Shape | Binding |
|
|
666
678
|
|---|---|---|---|
|
|
667
|
-
| Mutation | `$set` | `$set NAME = value` (with
|
|
679
|
+
| Mutation | `$set` | `$set NAME = value` (with `${VAR}` interpolation at bind) | NAME (no arrow) |
|
|
668
680
|
| Mutation | `$append` | `$append VAR <value>` (type-dispatched: list element / string concat) | VAR (no arrow) |
|
|
669
681
|
| Runtime-intrinsic | `emit` | `emit(text="...")` or `emit(text="""...""")` for multi-line | none |
|
|
670
682
|
| Runtime-intrinsic | `notify` | `notify(agent="...", [message=...], [event_type=...], [correlation_id=...]) -> ACK` | optional |
|
|
671
683
|
| Runtime-intrinsic | `inline` | `inline(skill="<name>")` | none (compile-time) |
|
|
672
|
-
| Runtime-intrinsic | `execute_skill` | `execute_skill(name="...", inputs
|
|
684
|
+
| Runtime-intrinsic | `execute_skill` | `execute_skill(name="...", inputs={...}) -> R` (`skill_name=` back-compat alias) | optional |
|
|
673
685
|
| 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 |
|
|
674
|
-
| Runtime-intrinsic | `file_read` | `file_read(path="...", [encoding="utf8"
|
|
686
|
+
| Runtime-intrinsic | `file_read` | `file_read(path="...", [encoding="utf8"|"base64"]) -> R` | required |
|
|
675
687
|
| Runtime-intrinsic | `file_write` | `file_write(path="...", content="...", [approved="..."])` | none |
|
|
676
688
|
| External MCP — substrate-specific | `$ <connector>.<tool>` | `$ <connector>.<tool> kwarg=value, ... [timeout=N] [approved="..."] [-> R] [(fallback: "...")]` | optional |
|
|
677
689
|
| External MCP — typed-contract | `$ <tool>` | `$ <tool> kwarg=value, ... [timeout=N] [approved="..."] [-> R] [(fallback: "...")]` (typed-contract ops only: `data_*`, `skill_*`, `json_parse`, `llm`) | optional |
|
|
@@ -698,13 +710,13 @@ Injected automatically at runtime; never declared by the author.
|
|
|
698
710
|
|
|
699
711
|
| Var | Value |
|
|
700
712
|
|-----|-------|
|
|
701
|
-
|
|
|
702
|
-
|
|
|
703
|
-
|
|
|
704
|
-
|
|
|
705
|
-
|
|
|
706
|
-
|
|
|
707
|
-
|
|
|
713
|
+
| `${NOW}` | ISO-8601 timestamp at op-dispatch time |
|
|
714
|
+
| `${USER}` | The configured user identity |
|
|
715
|
+
| `${SESSION_CONTEXT}` | Current session-scope context (project/entity/etc., substrate-defined) |
|
|
716
|
+
| `${TRIGGER_TYPE}` | What event fired this skill |
|
|
717
|
+
| `${TRIGGER_PAYLOAD}` | Event-specific data |
|
|
718
|
+
| `${EVENT.*}` | Event-payload fields populated by the trigger source |
|
|
719
|
+
| `${ERROR_CONTEXT}` | Inside a target's `else:` error-handler: kind + message + target of the failure. |
|
|
708
720
|
|
|
709
721
|
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.
|
|
710
722
|
|
|
@@ -886,14 +898,14 @@ Pipe filters apply transforms to resolved variables before substitution. Syntax:
|
|
|
886
898
|
|
|
887
899
|
| Filter | Effect | Example | Output |
|
|
888
900
|
|--------|--------|---------|--------|
|
|
889
|
-
| `url` | `encodeURIComponent(value)` |
|
|
890
|
-
| `shell` | POSIX single-quote escape with outer quotes |
|
|
891
|
-
| `json` | `JSON.stringify(value)` |
|
|
892
|
-
| `trim` | Whitespace trim |
|
|
893
|
-
| `length` | Count of items (array) or characters (string) |
|
|
894
|
-
| `contains:"X"` | Boolean: type-aware substring / element membership |
|
|
895
|
-
| `fallback:"X"` | Coalesce on missing/undefined/empty ref |
|
|
896
|
-
| `isodate` | Epoch seconds → ISO-8601 timestamp |
|
|
901
|
+
| `url` | `encodeURIComponent(value)` | `${location|url}` for `"Asheville, NC"` | `Asheville%2C%20NC` |
|
|
902
|
+
| `shell` | POSIX single-quote escape with outer quotes | `${arg|shell}` for `it's safe` | `'it'\''s safe'` |
|
|
903
|
+
| `json` | `JSON.stringify(value)` | `${payload|json}` for `{k:"v"}` | `"{\"k\":\"v\"}"` |
|
|
904
|
+
| `trim` | Whitespace trim | `${VERDICT|trim}` for `"urgent\n"` | `urgent` |
|
|
905
|
+
| `length` | Count of items (array) or characters (string) | `${ITEMS|length}` for `["a","b","c"]` | `3` |
|
|
906
|
+
| `contains:"X"` | Boolean: type-aware substring / element membership | `${MSG|contains:"urgent"}` for `"Yes, urgent"` | `true` |
|
|
907
|
+
| `fallback:"X"` | Coalesce on missing/undefined/empty ref | `${VAR.missing|fallback:"-"}` | `-` |
|
|
908
|
+
| `isodate` | Epoch seconds → ISO-8601 timestamp | `${EPOCH|isodate}` for `1779660000` | `2026-05-24T22:00:00.000Z` |
|
|
897
909
|
|
|
898
910
|
### `length` semantics
|
|
899
911
|
|
|
@@ -954,7 +966,7 @@ emit:
|
|
|
954
966
|
emit(text="nested: ${ISSUE.customFields.Assignee|fallback:\"unassigned\"}")
|
|
955
967
|
```
|
|
956
968
|
|
|
957
|
-
**Order-independent in a filter chain
|
|
969
|
+
**Order-independent in a filter chain.** A `|fallback` anywhere in the chain rescues an unresolved base — `${x|trim|fallback:"d"}` and `${x|fallback:"d"|trim}` both degrade an unresolved `x` to `"d"`. Nuance: it rescues a genuinely-unresolved/missing base; a **present-but-empty value still flows through the other filters**, so `${x|length|fallback:"0"}` on `x=" "` returns `"2"`, not `"0"`. See the Robustness & error containment section for the chain semantics.
|
|
958
970
|
|
|
959
971
|
**Why filter-shape, not ref-level `(fallback:)`.** Op-level `(fallback: ...)` exists on `$` dispatch and `shell()` for **error/empty recovery** (the op ran, then failed or returned empty). Ref-level `|fallback:` is **coalesce** (the lookup itself found nothing — missing, null, or empty). They rhyme but are adjacent concepts. The filter-chain attachment keeps composition clean (`${VAR|json_parse|fallback:"-"}` works as a chain step) and the vocabulary alignment with op-level `(fallback:)` lets cold authors learn "fallback" as the universal concept while the syntax disambiguates the attachment site.
|
|
960
972
|
|
|
@@ -1037,7 +1049,7 @@ Several filters are planned but not yet shipped:
|
|
|
1037
1049
|
| `summary` | One-line abbreviation | Compress for human-facing emissions |
|
|
1038
1050
|
| `pluck:<field>` | Project array of objects to array of field values | Paired with `in`/`not in` for dedup-by-id workflows |
|
|
1039
1051
|
| `join:"<sep>"` | List → string with separator | Filter-shape alternative to string `$append`; reconsider if filter-chain demand surfaces |
|
|
1040
|
-
| `isodate_ms` | Epoch ms → ISO-8601 | Companion to
|
|
1052
|
+
| `isodate_ms` | Epoch ms → ISO-8601 | Companion to `|isodate`; defer until demand |
|
|
1041
1053
|
|
|
1042
1054
|
`pluck` is the highest-priority remaining filter — it closes the structural-dedup gap for skills that iterate retrieval results and want to exclude already-seen items by ID without manual comparison loops.
|
|
1043
1055
|
|
|
@@ -1261,7 +1273,7 @@ run:
|
|
|
1261
1273
|
foreach D in ${DS}:
|
|
1262
1274
|
...
|
|
1263
1275
|
```
|
|
1264
|
-
An empty list (`DOMAINS='[]'`, or an empty/whitespace string) iterates zero times
|
|
1276
|
+
An empty list (`DOMAINS='[]'`, or an empty/whitespace string) iterates zero times. The comma-split affordance is for `# Vars:` DEFAULTS only, not runtime inputs — there is no `|split` filter (tracked as a DX enhancement).
|
|
1265
1277
|
|
|
1266
1278
|
## Triggers — # Triggers: header, declarative + imperative registration, source types
|
|
1267
1279
|
|
|
@@ -1798,6 +1810,14 @@ interface McpDispatchCtx {
|
|
|
1798
1810
|
|
|
1799
1811
|
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.
|
|
1800
1812
|
|
|
1813
|
+
## Writing a non-MCP connector — "MCP" names the verb, not the wire
|
|
1814
|
+
|
|
1815
|
+
`McpConnector` is the dispatch surface for `$ connector.tool` ops; the name refers to the skill-facing verb, **not** a wire-protocol requirement. The contract is just `call(toolName, args, ctxOverrides?)` plus an optional `describeTools()` — which surfaces the connector's tool set (inputSchema per tool) for authoring and lint. MCP JSON-RPC framing is `HttpMcpConnector`'s implementation detail, not part of the interface.
|
|
1816
|
+
|
|
1817
|
+
So a backend that speaks plain REST is first-class: `class RestConnector implements McpConnector` whose `call()` maps `tool + kwargs` to an HTTPS request (path-templating, query-vs-body routing, auth header) gets the full typed-contract surface — closed tool set for lint, per-tool kwarg validation, `$`-prefix state-affecting gating — with zero MCP framing. MCP and REST connectors coexist freely in `connectors.json`; a single skill body can use `$ gmail.send` (MCP) and `$ tickets.create` (REST) side by side.
|
|
1818
|
+
|
|
1819
|
+
Full contract spec + a runnable worked example: see `connector-contract-reference.md` (McpConnector section) and `examples/connectors/RestConnector/`.
|
|
1820
|
+
|
|
1801
1821
|
## Lifecycle and status — # Status: header, three canonical states (Draft / Approved / Disabled), compile + runtime enforcement
|
|
1802
1822
|
|
|
1803
1823
|
Skillscripts carry an explicit lifecycle state via the `# Status:` header. The compiler and runtime enforce it — a Disabled skill cannot fire under any path, and (in secured mode) an unapproved skill cannot perform effectful ops under any path.
|
|
@@ -1861,13 +1881,29 @@ Test, Deployed, and Deprecated were considered and deferred — today's Draft/Ap
|
|
|
1861
1881
|
|
|
1862
1882
|
Lifecycle states are the language's operational-safety answer. A Disabled skill can't fire even if every author forgets it's broken; in secured mode, an Approved skill can't fire if its body was tampered post-signature, and an agent can't approve its own work. The constraint IS the safety story, here as elsewhere.
|
|
1863
1883
|
|
|
1884
|
+
## Frontmatter tags — # Tags: classification metadata
|
|
1885
|
+
|
|
1886
|
+
`# Tags:` is optional frontmatter for **skill classification** — a comma-separated list of free-form labels used to organize and filter skills. It has no effect on execution, dispatch, or approval.
|
|
1887
|
+
|
|
1888
|
+
```
|
|
1889
|
+
# Tags: skill-classification, email, deterministic
|
|
1890
|
+
```
|
|
1891
|
+
|
|
1892
|
+
**Parsing.** The comma-list parses to a `tags: string[]` on the parsed skill. Omitted → empty list (`[]`), never null; every skill carries a `tags` array.
|
|
1893
|
+
|
|
1894
|
+
**Approval-neutral.** Tags are excluded from the approval signing hash — the signature is computed over the canonicalized body with the `# Tags:` line stripped, the same treatment as `# Status:`. Editing a skill's tags therefore does **not** invalidate its approval or force re-approval. Nothing security-relevant reads tags; they are organizational metadata only.
|
|
1895
|
+
|
|
1896
|
+
**Runtime-derived, surfaced on the contract.** Like `# Description:` and `# Vars:`, the runtime derives tags from parsed frontmatter, so a `SkillStore` that omits a dedicated tags field needs no change. Tags surface as `SkillMeta.tags` and on the `skill_list` / `skill_preflight` payloads (empty `[]` when untagged). Authoring surfaces (e.g. the dashboard) facet the skills list by tag — filter chips plus per-row pills.
|
|
1897
|
+
|
|
1898
|
+
**When to use.** Group related skills for discovery in a growing library (by domain, by delivery kind, by team). Tags complement the `# Description:` trigger-condition discipline: description drives *invocation selection*, tags drive *organization and filtering*.
|
|
1899
|
+
|
|
1864
1900
|
## Error handling — else: blocks and op-level (fallback:) values
|
|
1865
1901
|
|
|
1866
1902
|
Skillscript has no try/catch — error handling is authored. Two runtime mechanisms (local to global) plus a structural discipline that prevents failure outright.
|
|
1867
1903
|
|
|
1868
1904
|
## Layer 1: Target-level `else:` block — the throw-container
|
|
1869
1905
|
|
|
1870
|
-
Runs if any op in the target's primary body throws. Local to the failing target; downstream targets that depend on it can still proceed with whatever the `else:` branch produced. `${ERROR_CONTEXT}` (`.kind` / `.message` / `.target`) is available inside it. This is the way to contain a raised throw — including an `execute_skill` child-throw and a `$ json_parse` on malformed input
|
|
1906
|
+
Runs if any op in the target's primary body throws. Local to the failing target; downstream targets that depend on it can still proceed with whatever the `else:` branch produced. `${ERROR_CONTEXT}` (`.kind` / `.message` / `.target`) is available inside it. This is the way to contain a raised throw — including an `execute_skill` child-throw and a `$ json_parse` on malformed input.
|
|
1871
1907
|
|
|
1872
1908
|
```
|
|
1873
1909
|
fetch:
|
|
@@ -1879,9 +1915,9 @@ else:
|
|
|
1879
1915
|
|
|
1880
1916
|
Distinguished from conditional `else:` (which appears after an `if:`/`elif:` chain inside a body) by the parser's scope-stack; both can coexist in a target. An `else:` block may not declare its own error handler.
|
|
1881
1917
|
|
|
1882
|
-
## Layer 2: Op-level `(fallback:)` value — uniform
|
|
1918
|
+
## Layer 2: Op-level `(fallback:)` value — uniform
|
|
1883
1919
|
|
|
1884
|
-
Inline fallback on the op line.
|
|
1920
|
+
Inline fallback on the op line. It is **uniform: it contains ANY failure of that op** — a raised throw OR an empty/missing result. It works on `$` (MCP dispatch), `shell()`, `file_read`, `execute_skill` (child throw), and `$ json_parse` (malformed input). On failure the fallback value binds to the output var via the same path as a success (coerce-on-bind), so downstream sees it transparently and needs no "did this fail?" check. A fired fallback is recorded in `result.fallbacks[].reason` (not `result.errors[]`) — degrade-loud.
|
|
1885
1921
|
|
|
1886
1922
|
```
|
|
1887
1923
|
weather:
|
|
@@ -1910,7 +1946,7 @@ Pre-bind defaults (`$set`) for every var the template/downstream reads, then gat
|
|
|
1910
1946
|
|
|
1911
1947
|
## Deprecated: `# OnError:` — parsed but not wired
|
|
1912
1948
|
|
|
1913
|
-
A skill-level `# OnError: <fallback-skill>` header parses, but it is **not wired in the current runtime: the named fallback never fires.** A skill that relies on it has, in effect, no error handling — so don't use it. Use a target-level `else:` handler (or an op-level `(fallback:)`) instead.
|
|
1949
|
+
A skill-level `# OnError: <fallback-skill>` header parses, but it is **not wired in the current runtime: the named fallback never fires.** A skill that relies on it has, in effect, no error handling — so don't use it. Use a target-level `else:` handler (or an op-level `(fallback:)`) instead. The header is inert, not a compile error; removal is planned. To recover at the whole-skill level, wrap the body in a target with an `else:` that dispatches your recovery skill:
|
|
1914
1950
|
|
|
1915
1951
|
```
|
|
1916
1952
|
run:
|
|
@@ -1930,14 +1966,14 @@ else:
|
|
|
1930
1966
|
|
|
1931
1967
|
**Skillscript has no try/catch — by design. Robustness is authored, not caught.** An unguarded fallible op that fails aborts the whole target — and in a fan-out, aborts every sibling that hadn't run yet. You contain failures explicitly.
|
|
1932
1968
|
|
|
1933
|
-
**One uniform rule
|
|
1969
|
+
**One uniform rule: `(fallback:)` contains ANY op failure.** A `-> VAR (fallback: "<default>")` trailer catches whatever goes wrong with that op — a `$`/`shell` dispatch error, a `shell` spawn-fail/timeout, an empty result, AND a raised throw (`$ json_parse` on off-shape input, `execute_skill` whose child throws). The op degrades to the fallback value and the target continues.
|
|
1934
1970
|
|
|
1935
1971
|
**The containment tools — pick by how much you want to handle:**
|
|
1936
1972
|
| Tool | Catches | Scope |
|
|
1937
1973
|
|---|---|---|
|
|
1938
1974
|
| `(fallback: "…")` op trailer | ANY failure of that op — dispatch error / spawn-fail / timeout / empty result / raised throw | the single op |
|
|
1939
|
-
|
|
|
1940
|
-
| `else:` block | a raised throw anywhere in the target body, WITH error context (
|
|
1975
|
+
| `${ref\|fallback:"x"}` filter | a missing / unresolved / empty value at use-time | the single reference |
|
|
1976
|
+
| `else:` block | a raised throw anywhere in the target body, WITH error context (`${ERROR_CONTEXT.kind/.message/.target}`) | the whole target |
|
|
1941
1977
|
| structural guard (pre-bind defaults + a `contains`/shape check before the risky op) | PREVENTS the failure — the op isn't reached on bad input, so it can't fail | the risky op |
|
|
1942
1978
|
| `# OnError: <skill>` | INERT in the current runtime (fallbackSkillExecutor never wired) — do NOT rely on it; prefer `else:` | — |
|
|
1943
1979
|
|
|
@@ -1958,13 +1994,30 @@ default: fetch
|
|
|
1958
1994
|
```
|
|
1959
1995
|
Bad input skips the `if`, the default stands, the child never throws — so a parent gather can't be sunk by it (independent of the parent's own fallback).
|
|
1960
1996
|
|
|
1961
|
-
**Rule 3 — a `fallback` filter anywhere in a chain rescues an unresolved reference** (order-independent
|
|
1997
|
+
**Rule 3 — a `fallback` filter anywhere in a chain rescues an unresolved reference** (order-independent; also rescues a missing dotted/numeric path on a present object — `${W.a.0.b|fallback:"d"}` when `W` lacks `a` → `"d"`). A chain with NO `fallback` still throws on an unresolved ref — that's your signal to add one. It rescues only genuinely-unresolved refs; a present-but-empty value (`" "`) still flows through the other filters, so `${x|length|fallback:"0"}` on `" "` is `"2"`.
|
|
1962
1998
|
|
|
1963
1999
|
**Rule 4 — A body-text output template must not reference a var a fallible step might leave unset.** The template renders after the target runs; an unset var hard-fails the render (`Unresolved variable reference: $(AREA)`) — and that is exactly how a child skill throws OUT to its `execute_skill` parent. Pre-bind every template var to a default before the fallible step (this doubles as the throw-prevention structure in Rule 2), or source each with a `|fallback`.
|
|
1964
2000
|
|
|
1965
2001
|
**Rule 5 — Degrade LOUD, not silent.** A fallback value should be a visible marker ("unavailable", "—", "n/a") — never empty, never a plausible-but-wrong value. A degraded run must be diagnosable: the throw's reason is preserved in `result.fallbacks[].reason`, so a degraded leg stays traceable. Don't silently ship a blank or a fake reading. Clean-degrade beats both a hard abort and a silent lie.
|
|
1966
2002
|
|
|
1967
|
-
|
|
2003
|
+
## Deadlines & cancellation — # Deadline: bound, uncatchable termination, effect-boundary onAbort, uncertain effects
|
|
2004
|
+
|
|
2005
|
+
A skill can bound its entire run in wall-clock time with the `# Deadline: N` frontmatter header (N seconds; an integer or a `$(VAR)` reference). It is **opt-in** — a skill with no `# Deadline:` keeps ordinary per-op timeout behavior and carries no run-total bound. `# Deadline:` is part of the signing hash: editing it changes the safety envelope an approver signed, so it returns the skill to Draft for re-approval (the same treatment as any behavior-affecting header, and the opposite of approval-neutral headers like `# Status:` / `# Tags:`).
|
|
2006
|
+
|
|
2007
|
+
**One propagating bound.** The deadline resolves once, at the root run, to an absolute wall-clock instant. It propagates unchanged into composed child skills (`execute_skill`): a child inherits the parent's *remaining* budget and may only **tighten** it — its own `# Deadline:` is clamped to `min(inherited remaining, child budget)`, never loosening the root bound. A deep composition tree is therefore bounded by the root deadline as a whole; the total cannot exceed it regardless of nesting depth.
|
|
2008
|
+
|
|
2009
|
+
**Per-op interaction.** Each op's effective timeout is `min(per-op # Timeout:, remaining-to-deadline)`. A per-op `# Timeout:` survives as a tighter *local* bound; the deadline is the ceiling above it. An op that would dispatch with no time left fails fast before dispatching rather than starting work it cannot finish.
|
|
2010
|
+
|
|
2011
|
+
**Uncatchable termination.** When the deadline is reached the run raises a fatal `RunDeadlineExceeded` that is **not catchable** by op-level `(fallback:)` or target `else:` — it bypasses both, terminates the run, and returns the partial results gathered so far (emissions and effects) alongside a `deadlineExceeded` marker and the uncertain-effects log. This is deliberate. A *catchable* deadline would let a fallback-laden skill cascade every remaining op into instant-fail-then-fallback and hand back a result that *looks* complete but ran past the bound. Uncatchable is what makes "bounded to T" literally true: at the deadline, the run stops.
|
|
2012
|
+
|
|
2013
|
+
**Cancellation — keyed on the effect boundary.** When an op is cut at the deadline the runtime aborts the in-flight call. What that actually achieves is a property the connector declares — its **effect boundary**:
|
|
2014
|
+
|
|
2015
|
+
- **Call-bounded** (HTTP request/response, id-correlated RPC — the common case): aborting the call ends the effect (the socket closes, or a late correlated reply is dropped). Self-cleaning; nothing else to do.
|
|
2016
|
+
- **Outlives-call** (a physical actuator, a spawned or async job, an open stream/session, a held lease): aborting the *client* call does not stop the underlying effect. Such a connector must supply a bounded `onAbort(budgetMs)` hook — a compensating action (for example, a safety-stop on a physical device) that the runtime invokes within a small reserved cleanup budget before dropping the op. The reserve is taken only for outlives-call ops, so call-bounded ops pay nothing for it. A connector that declares outlives-call effects but provides no `onAbort` is **refused at registration**: an out-of-band effect that cannot be cancelled must not be wired.
|
|
2017
|
+
|
|
2018
|
+
`onAbort` is a **safety-stop, not a rollback**. It halts a runaway out-of-band effect; it does not undo what already happened. A *mutating* op cut mid-flight is therefore recorded in the run's **uncertain-effects** log as "issued, outcome uncertain" — never reported as cleanly cancelled. The caller (or, for an autonomous run, the durable trace) can see that the effect may have partially landed. Read-only ops carry no such uncertainty and are excluded.
|
|
2019
|
+
|
|
2020
|
+
**Guidance.** A skill that dispatches any effectful op while declaring no `# Deadline:` is unbounded at the run level; a lint (`unbounded-no-deadline`) advises adding one. Give effectful skills an explicit deadline — especially anything that drives a physical device or a long-running external job. Be aware that a tight deadline combined with an outlives-call op means that op will not *start* inside the final cleanup-reserve window: it fails fast rather than begin an effect it could not guarantee cleaning up in time.
|
|
1968
2021
|
|
|
1969
2022
|
## Composition — skills calling skills
|
|
1970
2023
|
|
|
@@ -2027,10 +2080,6 @@ default: fetch
|
|
|
2027
2080
|
|
|
2028
2081
|
**Top-level MCP result is filtered too.** A direct `execute_skill` MCP call of a no-`# Returns:` skill returns `final_vars: {}` — the filter applies to direct invocation, not just child-propagation. Adopters inspecting `final_vars` via the MCP tool see the declared-returns surface, not the full variable dump.
|
|
2029
2082
|
|
|
2030
|
-
**Lint:**
|
|
2031
|
-
- `unknown-returns-ref` (tier-1) — `# Returns: X` where `X` isn't bound anywhere in the skill body. Same shape as undeclared-var, for the export side.
|
|
2032
|
-
- `unexported-final-var-access` (tier-2 advisory) — caller accesses `${R.X}` where `X` isn't in the called skill's `# Returns:`. Catches the "forgot to export it" footgun (forward-reference deferred-resolution if the called skill isn't yet stored).
|
|
2033
|
-
|
|
2034
2083
|
## Semantics
|
|
2035
2084
|
|
|
2036
2085
|
**Skill resolution.** Missing skills produce a clean structured error (`MissingSkillReferenceError extends OpError`) — the parent's `(fallback: ...)` discipline applies if specified, otherwise a target-level `else:` handler catches it if declared, otherwise the parent fails with the error propagated through.
|
|
@@ -2047,7 +2096,7 @@ default: fetch
|
|
|
2047
2096
|
|
|
2048
2097
|
Two non-obvious behaviors when reading a child's failures from the parent (both confirmed by the v1.0 runtime-semantics test battery):
|
|
2049
2098
|
|
|
2050
|
-
- **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: ...)` / `else:` — those fire on the structured error regardless of nesting depth.
|
|
2099
|
+
- **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: ...)` / `else:` — those fire on the structured error regardless of nesting depth. The op-level `(fallback: ...)` trailer is uniform: it contains a raised `execute_skill` child-throw (recursion-guard fire, missing-skill, or any error nested in the child), so `execute_skill(...) -> R (fallback: "...")` reliably degrades on a child failure regardless of nesting depth, and the fired fallback lands in `result.fallbacks[].reason`.
|
|
2051
2100
|
- **`# 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.
|
|
2052
2101
|
|
|
2053
2102
|
## Forward-reference resolution
|
|
@@ -2143,7 +2192,7 @@ call_maybe_missing:
|
|
|
2143
2192
|
default: call_maybe_missing
|
|
2144
2193
|
```
|
|
2145
2194
|
|
|
2146
|
-
|
|
2195
|
+
The `(fallback:)` trailer is uniform, so it contains an `execute_skill` child-throw (missing-skill, recursion-guard fire, or any error raised inside the child) as well as a missing/empty bind — `RESULT` degrades to `"child unavailable"` on any of those, and the fired fallback is recorded in `result.fallbacks[].reason`.
|
|
2147
2196
|
|
|
2148
2197
|
**TestFlight preview (from the runtime caller, not from inside a skill):**
|
|
2149
2198
|
|
|
@@ -2167,7 +2216,7 @@ For *data skills* (skills marked `# Type: data`), the compile-time inline primit
|
|
|
2167
2216
|
- 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.
|
|
2168
2217
|
- 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.
|
|
2169
2218
|
- 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: []`.
|
|
2170
|
-
- Pair composition with `(fallback: ...)` when the child skill might fail and the parent has a sensible degraded path.
|
|
2219
|
+
- Pair composition with `(fallback: ...)` when the child skill might fail and the parent has a sensible degraded path. The `(fallback:)` catches a raised child throw, not just a missing bind.
|
|
2171
2220
|
- Use mechanical mode to TestFlight any multi-skill chain before shipping it as a Headless skill on a cron trigger.
|
|
2172
2221
|
- Forward references work — author sibling skills in any order, validate independently. The tier-2 warning surfaces the deferred-resolution path; runtime catches genuine misses.
|
|
2173
2222
|
- Recursion is legal but bounded. If your design requires deeper recursion than the configured limit, reshape the workflow — almost always a sign of an iteration that should be expressed as `foreach` rather than recursion.
|
|
@@ -2565,5 +2614,5 @@ When any of these primitives ship, the relevant grammar moves into its canonical
|
|
|
2565
2614
|
|
|
2566
2615
|
---
|
|
2567
2616
|
|
|
2568
|
-
*Rendered from `skillscript/skillscript-language-reference` — 2026-07-
|
|
2569
|
-
*Source of truth: AMP (`amp_render_document("skillscript/skillscript-language-reference")`)*
|
|
2617
|
+
*Rendered from `skillscript/skillscript-language-reference` — 2026-07-18 14:42 EDT*
|
|
2618
|
+
*Source of truth: AMP (`amp_render_document("skillscript/skillscript-language-reference")`)*
|
|
@@ -9,12 +9,12 @@ 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`, `SkillStoreMcpConnector` (in `src/connectors/`) |
|
|
12
|
+
| `McpConnector` | `HttpMcpConnector`, `RemoteMcpConnector`, `CallbackMcpConnector`, `LocalModelMcpConnector`, `DataStoreMcpConnector`, `SkillStoreMcpConnector` (in `src/connectors/`) | **[RestConnector/](./RestConnector/)** | **[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.
|
|
16
16
|
|
|
17
|
-
**Worked examples** are real implementations for substrates that aren't bundled — copy + customize for your specific deployment. HttpWebhookAgentConnector demonstrates the AgentConnector contract against a generic HTTP-webhook substrate.
|
|
17
|
+
**Worked examples** are real implementations for substrates that aren't bundled — copy + customize for your specific deployment. HttpWebhookAgentConnector demonstrates the AgentConnector contract against a generic HTTP-webhook substrate. RestConnector demonstrates that the McpConnector contract is wire-protocol-agnostic — it fronts a plain REST/HTTP API (no MCP wire protocol), and coexists in the same registry with actual MCP connectors.
|
|
18
18
|
|
|
19
19
|
**Fork templates** are skeletons (every method throws TODO). Useful when you want the bare contract surface without any specific substrate assumptions. SkillStoreTemplate is the starting point for Postgres-, MongoDB-, AMP-, or vector-DB-backed SkillStore impls.
|
|
20
20
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# RestConnector — example .env config.
|
|
2
|
+
# Copy to .env in your project root + fill in real values. Never commit the token.
|
|
3
|
+
#
|
|
4
|
+
# This connector reads its token from the env var named by `authTokenEnvVar`
|
|
5
|
+
# in your wiring. The example wires:
|
|
6
|
+
# new RestConnector({ baseUrl: "...", authTokenEnvVar: "TICKETS_API_TOKEN" })
|
|
7
|
+
# so it looks up process.env.TICKETS_API_TOKEN at call time.
|
|
8
|
+
#
|
|
9
|
+
# Rename the var to match your service; keep the wiring and the var name in sync.
|
|
10
|
+
TICKETS_API_TOKEN=
|
|
11
|
+
|
|
12
|
+
# The connector's `baseUrl` is passed in code / connectors.json, not here — but
|
|
13
|
+
# if you templatize connectors.json with env expansion, you might also keep:
|
|
14
|
+
# TICKETS_API_BASE_URL=https://api.internal.acme.io/v1
|
|
15
|
+
|
|
16
|
+
# Credential-free egress alternative: if you deploy behind an outbound proxy
|
|
17
|
+
# that injects auth, leave TICKETS_API_TOKEN empty, drop authTokenEnvVar from
|
|
18
|
+
# the wiring, and point baseUrl at the gateway. No secret lives in the runtime.
|
|
19
|
+
# See docs/adopter-playbook.md (proxy-egress pattern).
|