skillscript-runtime 0.13.2 → 0.13.6
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 +6 -4
- package/dist/connectors/sqlite-skill-store.d.ts.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +15 -3
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/dashboard/spa/app.js +9 -1
- package/dist/mcp-server.d.ts +3 -2
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +24 -2
- package/dist/mcp-server.js.map +1 -1
- package/docs/ERD.md +1139 -0
- package/docs/adopter-playbook.md +241 -0
- package/docs/configuration.md +326 -0
- package/docs/connector-contract-reference.md +164 -0
- package/docs/language-reference.md +1972 -0
- package/docs/sqlite-skill-store.md +167 -0
- package/examples/skillscripts/hello.skill.provenance.json +1 -1
- package/package.json +4 -3
- package/ARCHITECTURE.md +0 -102
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Connector Contract Reference
|
|
2
|
+
|
|
3
|
+
The substrate-neutral contracts skillscript-runtime exposes for adopters to wire their own substrate behind. This doc is the **canonical source of truth** for the AgentConnector contract as locked at v1.0 by the v0.9.6 audit (Perry's thread `b722bbf4`).
|
|
4
|
+
|
|
5
|
+
**Audience**: this doc is written for the agent that's implementing an adopter's AgentConnector — typically an LLM-class agent supervised by a human. If you're a human reading it directly, the same content applies; the prose is tightened for agent comprehension (literal field semantics, explicit precedence rules, worked examples).
|
|
6
|
+
|
|
7
|
+
Other contracts (McpConnector, SkillStore, MemoryStore, LocalModel) audit + lock in subsequent v0.9.x slots; this doc grows with each lock.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## AgentConnector — v1.0 contract (locked v0.9.6)
|
|
12
|
+
|
|
13
|
+
### Purpose
|
|
14
|
+
|
|
15
|
+
Substrate-neutral delivery of payloads to a *frontier agent*. The runtime calls into the contract; the adopter implements the substrate (webhook, tmux session, file drop, IPC pipe, Slack thread, whatever).
|
|
16
|
+
|
|
17
|
+
The contract is intentionally minimal. Every required method represents a thing the adopter must implement correctly for their substrate. The runtime fills `DeliveryMeta` envelope on every `deliver()` call — adopters CONSUME meta (substrate-side translation), they NEVER CONSTRUCT it.
|
|
18
|
+
|
|
19
|
+
### Interface
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
interface AgentConnector {
|
|
23
|
+
list_agents(): Promise<AgentDescriptor[]>;
|
|
24
|
+
deliver(agent_id: string, payload: DeliveryPayload): Promise<DeliveryReceipt>;
|
|
25
|
+
wake(agent_id: string, opts?: WakeOpts): Promise<WakeReceipt>;
|
|
26
|
+
health_check(): Promise<boolean>;
|
|
27
|
+
request_response(agent_id: string, payload: DeliveryPayload, opts: RequestResponseOpts): Promise<Response>;
|
|
28
|
+
agent_status?(agent_id: string): Promise<AgentStatus>;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Required**: `list_agents`, `deliver`, `wake`, `health_check`, `request_response`.
|
|
33
|
+
**Optional**: `agent_status`.
|
|
34
|
+
|
|
35
|
+
`request_response` is locked at v1.0 for the planned `exchange()` op. Until the runtime support lands, adopters should throw `NotImplementedError` from this method (see `NoOpAgentConnector` for the canonical pattern).
|
|
36
|
+
|
|
37
|
+
### DeliveryPayload + DeliveryMeta
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
type DeliveryPayload =
|
|
41
|
+
| { kind: "augment"; content: string; meta: DeliveryMeta }
|
|
42
|
+
| { kind: "template"; prompt: string; meta: DeliveryMeta };
|
|
43
|
+
|
|
44
|
+
interface DeliveryMeta {
|
|
45
|
+
dispatch_id: string; // UUID per emit; same across broadcast branches
|
|
46
|
+
sent_at: number; // unix ms — runtime emit-clock
|
|
47
|
+
origin: {
|
|
48
|
+
skill_name: string;
|
|
49
|
+
entry_skill_name?: string;
|
|
50
|
+
trigger_kind: "cron" | "session" | "webhook" | "agent" | "cli" | "dashboard" | "inline";
|
|
51
|
+
caller_agent_id?: string;
|
|
52
|
+
};
|
|
53
|
+
event_type?: string;
|
|
54
|
+
correlation_id?: string;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Field semantics (read each carefully — these are the agent-actionable contract)
|
|
59
|
+
|
|
60
|
+
- **`kind`**: `"augment"` = context to absorb; `"template"` = playbook to execute. Closed set for v1.0. If a future minor adds `kind: "binary"` (or similar), the adopter substrate that can't handle it throws — substrate-side validation, not runtime concern.
|
|
61
|
+
|
|
62
|
+
- **`meta.dispatch_id`**: unique-per-emit identifier. Used by receivers for substrate-retry idempotency. **Rule: one `notify()` op invocation = one dispatch_id.** Multi-connector broadcast (one `notify()` op, N wired connectors for the same `agent_id`) share the same `dispatch_id` across all N `deliver()` calls. Sequential `notify()` calls produce distinct dispatch_ids per call. Author's call-site boundary is what defines the dispatch event.
|
|
63
|
+
|
|
64
|
+
- **`meta.sent_at`**: runtime emit-clock timestamp (unix ms). When `notify()` / `# Output:` fired — NOT when the substrate confirmed delivery. Distinct from receipt-side `delivered_at`. Staleness checks need both timestamps: `delivered_at - sent_at` = effective substrate queue lag.
|
|
65
|
+
|
|
66
|
+
- **`meta.origin.skill_name`**: immediate emitter. The skill that called `notify()` or fired `# Output: agent:`.
|
|
67
|
+
|
|
68
|
+
- **`meta.origin.entry_skill_name`**: root entry-point skill when distinct from `skill_name`. Set when emit happens inside a composed helper (e.g., A inlines B via `&`, B emits → `skill_name=B, entry_skill_name=A`). Intermediate composition steps (A→B→C) are NOT captured here — C's emit shows `skill_name=C, entry_skill_name=A`; B is in runtime trace logs, not the envelope.
|
|
69
|
+
|
|
70
|
+
- **`meta.origin.trigger_kind`**: how the originating skill was fired. Receiver routes on this without parsing content (cron-fired triage vs agent-initiated request vs webhook from external system).
|
|
71
|
+
|
|
72
|
+
- **`meta.origin.caller_agent_id`**: root-trigger agent IF identifiable, else undefined. The general rule: if the chain was initiated by an agent, that agent is the caller regardless of how deep the call stack is when the emit happens. Cron / session / cli / dashboard / inline triggers leave it undefined.
|
|
73
|
+
|
|
74
|
+
- **`meta.event_type`**: adopter-defined routing vocabulary — opaque to skillscript. Set via `notify(event_type=...)` kwarg (per-emit) OR `# Event-type:` skill frontmatter (skill-wide fallback). Kwarg takes precedence per-emit.
|
|
75
|
+
|
|
76
|
+
- **`meta.correlation_id`**: reply-correlation for the future `exchange()` op / `request_response()` substrate path. Sender sets; receiver echoes on reply. Kind-independent — both augment and template payloads may carry it.
|
|
77
|
+
|
|
78
|
+
### DeliveryReceipt
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface DeliveryReceipt {
|
|
82
|
+
delivered_at: number;
|
|
83
|
+
delivery_id?: string;
|
|
84
|
+
delivery_skipped?: boolean;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **`delivered_at`**: substrate-acknowledgement timestamp. When the substrate confirmed it accepted the delivery.
|
|
89
|
+
- **`delivery_id`**: substrate-specific id for callers to correlate later.
|
|
90
|
+
- **`delivery_skipped`**: adopter signals "accepted but not pushed to the agent" — offline, rate-limit drop, tmux session exists but agent hasn't read, etc. Distinct from outright failure (which throws). Runtime echoes this on the receipt record for dashboard observability.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Use-site cross-reference table
|
|
95
|
+
|
|
96
|
+
| Language surface | Runtime method | DeliveryPayload kind | meta sourced from |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `# Output: agent: X` lifecycle hook | `AgentConnector.deliver()` | `augment` | Frontmatter `# Event-type:` (if set); `event_type` & `correlation_id` always undefined |
|
|
99
|
+
| `# Output: template: X` lifecycle hook | `AgentConnector.deliver()` | `template` | Same as above |
|
|
100
|
+
| `notify(agent=X, message=..., event_type=..., correlation_id=...)` op | `AgentConnector.deliver()` | `augment` | Kwargs override frontmatter for `event_type`; `correlation_id` from kwarg only |
|
|
101
|
+
| `exchange(agent=X, message=..., timeout=...)` op (locked-shape, runtime support pending) | `AgentConnector.request_response()` | `augment` | Same as notify; correlation_id required |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Adopter wiring canonical pattern
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { Registry } from "skillscript-runtime";
|
|
109
|
+
import { MyHttpWebhookAgentConnector } from "./my-impls/http-webhook.js";
|
|
110
|
+
|
|
111
|
+
const registry = new Registry();
|
|
112
|
+
|
|
113
|
+
// registerAgentConnector is async — bootstrap-throws on health_check() returning false
|
|
114
|
+
await registry.registerAgentConnector("primary", new MyHttpWebhookAgentConnector({
|
|
115
|
+
endpoint: "https://my-agent.example.com/inbox",
|
|
116
|
+
api_key: process.env.MY_AGENT_API_KEY,
|
|
117
|
+
}));
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Wiring failures surface at boot (health_check throws), not at first skill-fire. Adopters wanting soft dev-mode behavior wrap the connector with a retry/always-healthy shim; the contract stays clean.
|
|
121
|
+
|
|
122
|
+
### Writing your own AgentConnector
|
|
123
|
+
|
|
124
|
+
If you're an agent implementing this contract against an adopter substrate, the canonical worked example is `HttpWebhookAgentConnector` (shipping post-audit; see `examples/` once bundled).
|
|
125
|
+
|
|
126
|
+
Implementation checklist:
|
|
127
|
+
|
|
128
|
+
1. **Implement `list_agents()`** — return the set of agent ids your substrate knows about. If your substrate is single-agent (e.g., a fixed webhook), return one. If it's multi-agent (e.g., a registry of webhook URLs keyed by agent_id), return all.
|
|
129
|
+
|
|
130
|
+
2. **Implement `deliver(agent_id, payload)`** — serialize `payload` to your substrate's format. For HTTP: JSON body with `kind`, `content`/`prompt`, and `meta`. For tmux: serialize meta as a header line, write content via `tmux send-keys`. For file-drop: write a file under `<dir>/<dispatch_id>.{json,txt}`.
|
|
131
|
+
|
|
132
|
+
3. **Implement `wake(agent_id, opts?)`** — substrate-specific "rouse the agent." Webhook: POST to a `/wake` endpoint. Tmux: send a wake-up sequence. Etc.
|
|
133
|
+
|
|
134
|
+
4. **Implement `health_check()`** — return `true` if substrate is reachable + configured. Webhook: HEAD/OPTIONS your endpoint. Tmux: check the session exists. File-drop: check the directory is writable.
|
|
135
|
+
|
|
136
|
+
5. **Implement `request_response()`** — throw `NotImplementedError` until the runtime support for `exchange()` lands. When it does, and your substrate supports synchronous reply, implement the contract: send payload, await reply matched by `correlation_id`, time out per `opts.timeout_ms`.
|
|
137
|
+
|
|
138
|
+
6. **Optional: implement `agent_status?()`** — return `"active"` / `"idle"` / `"asleep"` / `"unknown"` per agent. Pure metadata; runtime does NOT gate delivery on this value (skip delivery via `delivery_skipped: true` on the receipt instead).
|
|
139
|
+
|
|
140
|
+
### Forking / customizing the bundled connectors
|
|
141
|
+
|
|
142
|
+
If your substrate matches the shape of a bundled connector closely (e.g., HTTP webhook with a tweaked auth header), forking `HttpWebhookAgentConnector` is acceptable. To keep upstream merges painless:
|
|
143
|
+
|
|
144
|
+
- Don't touch `src/connectors/agent.ts` (contract) — that's the highest-merge-cost surface
|
|
145
|
+
- Fork `src/connectors/agent-noop.ts` or `src/connectors/agent-http-webhook.ts` into your own file; register YOUR fork via `registry.registerAgentConnector()`
|
|
146
|
+
- Stay on the `AgentConnector` interface — don't add methods; if you need substrate-specific helpers, make them adopter-local
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Footnotes pinned during the v0.9.6 audit (Perry's thread b722bbf4)
|
|
151
|
+
|
|
152
|
+
These are the load-bearing semantic rules. Internalize before implementing.
|
|
153
|
+
|
|
154
|
+
1. **dispatch_id — broadcast vs sequential**: one `notify()` op invocation = one dispatch_id. Multi-connector broadcast (same agent_id across N wired connectors) shares; sequential `notify()` calls produce distinct ids. Author's call-site boundary defines the dispatch event.
|
|
155
|
+
|
|
156
|
+
2. **entry_skill_name — deeper-than-2-level chains lose middle**: A→B→C, C emits → `skill_name=C, entry_skill_name=A`. B is in runtime trace logs, NOT the envelope. Surface boundaries are decisions, not accidents.
|
|
157
|
+
|
|
158
|
+
3. **caller_agent_id — general rule**: root-trigger agent IF identifiable, else undefined. All substrate-specific cases (cron/session/webhook/agent/cli/dashboard/inline) drop out cleanly from this rule. Cron / session / cli / dashboard / inline trigger paths leave it undefined.
|
|
159
|
+
|
|
160
|
+
4. **sent_at vs delivered_at**: `meta.sent_at` is the runtime's emit-clock (when `notify()` / `# Output:` fired). Receipt-side `delivered_at` is the substrate's acknowledgement timestamp. Substrate-side queueing may mean significant gaps (file-drop poller intervals, webhook retries, broker buffering). Adopters running staleness checks need both surfaces; `delivered_at - sent_at` = effective queue lag.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
*This doc reflects the v0.9.6 lock; future contract changes update this file alongside the code.*
|