@dot-skill/runtime 0.4.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bharat Dudeja
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ import type { CapabilityAdapterHint, CapabilityRequirement, InputSlot, RuntimeMode, SkillPackageFiles, SkillRun, TrustProfile } from "@dot-skill/protocol";
2
+ import { inspectSkill, unpackSkill, validatePackageBytes } from "@dot-skill/core";
3
+ export interface CapabilityAdapter {
4
+ name: string;
5
+ supports: (cap: CapabilityRequirement) => boolean;
6
+ invoke: (cap: CapabilityRequirement, args: Record<string, unknown>) => Promise<{
7
+ ok: boolean;
8
+ result?: unknown;
9
+ error?: string;
10
+ adapter: CapabilityAdapterHint;
11
+ }>;
12
+ }
13
+ export interface RuntimeHost {
14
+ askInputs?: (slots: InputSlot[]) => Promise<Record<string, unknown>>;
15
+ consent?: (plan: {
16
+ title: string;
17
+ permissions: string[];
18
+ steps: string[];
19
+ }) => Promise<boolean>;
20
+ resolveSecret?: (ref: string) => Promise<string>;
21
+ env?: Record<string, string>;
22
+ adapters?: CapabilityAdapter[];
23
+ model?: string;
24
+ host?: string;
25
+ }
26
+ export interface RunOptions {
27
+ mode?: RuntimeMode;
28
+ inputs?: Record<string, unknown>;
29
+ resume_from?: string;
30
+ checkpoint_state?: Record<string, unknown>;
31
+ /** Defaults to manifest policy. Use open only for untrusted drafts. */
32
+ trust_profile?: TrustProfile;
33
+ /** Reference verifier only; production runtimes should use a real trust store. */
34
+ issuer_secret?: string;
35
+ }
36
+ export declare function explainPackage(pkg: SkillPackageFiles): {
37
+ title: string;
38
+ description: string;
39
+ inputs: InputSlot[];
40
+ permissions: string[];
41
+ steps: Array<{
42
+ id: string;
43
+ kind: string;
44
+ title?: string;
45
+ }>;
46
+ constraints: string[];
47
+ };
48
+ export declare function runSkillPackage(pkg: SkillPackageFiles, host?: RuntimeHost, options?: RunOptions): Promise<SkillRun>;
49
+ export declare function runSkillArchive(archive: Uint8Array, host?: RuntimeHost, options?: RunOptions): Promise<SkillRun>;
50
+ export { inspectSkill, validatePackageBytes, unpackSkill, explainPackage as explain };
package/dist/index.js ADDED
@@ -0,0 +1,605 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { inspectSkill, unpackSkill, validatePackageBytes, verifyMintTrust, sha256Digest, } from "@dot-skill/core";
3
+ const RUNTIME_NAME = "@dot-skill/runtime";
4
+ const RUNTIME_VERSION = "0.4.1";
5
+ function substitute(template, inputs) {
6
+ return template.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g, (_, name) => {
7
+ const v = inputs[name];
8
+ return v === undefined || v === null ? `{{${name}}}` : String(v);
9
+ });
10
+ }
11
+ function missingInputs(slots, provided, env) {
12
+ return slots.filter((slot) => {
13
+ if (!slot.required && slot.ask_when !== "always")
14
+ return false;
15
+ if (slot.ask_when === "never")
16
+ return false;
17
+ if (provided[slot.name] !== undefined)
18
+ return false;
19
+ if (slot.default !== undefined)
20
+ return false;
21
+ if (slot.source === "environment" && env[slot.name] !== undefined)
22
+ return false;
23
+ if (slot.ask_when === "if_missing" || slot.ask_when === "always" || slot.required) {
24
+ return true;
25
+ }
26
+ return false;
27
+ });
28
+ }
29
+ function resolveInputsSync(slots, provided, env) {
30
+ const resolved = { ...provided };
31
+ const secret_refs = {};
32
+ for (const slot of slots) {
33
+ if (resolved[slot.name] !== undefined) {
34
+ if (slot.sensitivity === "secret") {
35
+ secret_refs[slot.name] = String(resolved[slot.name]);
36
+ resolved[slot.name] = `secret:${slot.name}`;
37
+ }
38
+ continue;
39
+ }
40
+ if (slot.source === "environment" && env[slot.name] !== undefined) {
41
+ resolved[slot.name] = env[slot.name];
42
+ continue;
43
+ }
44
+ if (slot.default !== undefined) {
45
+ resolved[slot.name] = slot.default;
46
+ }
47
+ }
48
+ return { resolved, secret_refs, missing: missingInputs(slots, resolved, env) };
49
+ }
50
+ export function explainPackage(pkg) {
51
+ return {
52
+ title: pkg.manifest.title,
53
+ description: pkg.manifest.description,
54
+ inputs: pkg.manifest.inputs,
55
+ permissions: pkg.manifest.permissions.map((p) => `${p.side_effect_class}: ${p.description}`),
56
+ steps: pkg.workflow.steps.map((s) => ({ id: s.id, kind: s.kind, title: s.title })),
57
+ constraints: (pkg.workflow.constraints ?? []).map((c) => `${c.effect}: ${c.statement}`),
58
+ };
59
+ }
60
+ export async function runSkillPackage(pkg, host = {}, options = {}) {
61
+ const mode = options.mode ?? "execute";
62
+ const started = new Date().toISOString();
63
+ const runId = `run_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
64
+ const stepRecords = [];
65
+ const verifications = [];
66
+ const outputs = {};
67
+ const stepOutputs = { ...(options.checkpoint_state ?? {}) };
68
+ const env = host.env ?? Object.fromEntries(Object.entries(process.env).filter((e) => e[1] !== undefined));
69
+ let { resolved, secret_refs, missing } = resolveInputsSync(pkg.manifest.inputs, options.inputs ?? {}, env);
70
+ if (missing.length && host.askInputs && mode !== "inspect" && mode !== "explain") {
71
+ const answered = await host.askInputs(missing);
72
+ ({ resolved, secret_refs, missing } = resolveInputsSync(pkg.manifest.inputs, { ...resolved, ...answered }, env));
73
+ }
74
+ if (mode === "inspect" || mode === "explain") {
75
+ return {
76
+ kind: "skill_run",
77
+ id: runId,
78
+ skill_id: pkg.manifest.id,
79
+ skill_version: pkg.manifest.version,
80
+ package_digest: pkg.manifest.package_digest,
81
+ status: missing.length ? "paused" : "succeeded",
82
+ mode,
83
+ resolved_inputs: resolved,
84
+ secret_refs,
85
+ steps: [],
86
+ outputs: { explanation: explainPackage(pkg), missing_inputs: missing.map((m) => m.name) },
87
+ verifications: [],
88
+ runtime: {
89
+ name: RUNTIME_NAME,
90
+ version: RUNTIME_VERSION,
91
+ host: host.host,
92
+ model: host.model,
93
+ },
94
+ started_at: started,
95
+ finished_at: new Date().toISOString(),
96
+ };
97
+ }
98
+ if (missing.length) {
99
+ return {
100
+ kind: "skill_run",
101
+ id: runId,
102
+ skill_id: pkg.manifest.id,
103
+ skill_version: pkg.manifest.version,
104
+ package_digest: pkg.manifest.package_digest,
105
+ status: "paused",
106
+ mode,
107
+ resolved_inputs: resolved,
108
+ secret_refs,
109
+ steps: [],
110
+ verifications: [],
111
+ runtime: {
112
+ name: RUNTIME_NAME,
113
+ version: RUNTIME_VERSION,
114
+ host: host.host,
115
+ model: host.model,
116
+ },
117
+ started_at: started,
118
+ finished_at: new Date().toISOString(),
119
+ error: `Missing required inputs: ${missing.map((m) => m.name).join(", ")}`,
120
+ };
121
+ }
122
+ const consentNeeded = pkg.manifest.permissions
123
+ .filter((p) => p.requires_consent)
124
+ .map((p) => p.side_effect_class);
125
+ if (mode === "execute" && consentNeeded.length && host.consent) {
126
+ const allowed = await host.consent({
127
+ title: pkg.manifest.title,
128
+ permissions: consentNeeded,
129
+ steps: pkg.workflow.steps.map((s) => `${s.id}:${s.kind}`),
130
+ });
131
+ if (!allowed) {
132
+ return {
133
+ kind: "skill_run",
134
+ id: runId,
135
+ skill_id: pkg.manifest.id,
136
+ skill_version: pkg.manifest.version,
137
+ package_digest: pkg.manifest.package_digest,
138
+ status: "cancelled",
139
+ mode,
140
+ resolved_inputs: resolved,
141
+ secret_refs,
142
+ steps: [],
143
+ verifications: [],
144
+ runtime: {
145
+ name: RUNTIME_NAME,
146
+ version: RUNTIME_VERSION,
147
+ host: host.host,
148
+ model: host.model,
149
+ },
150
+ started_at: started,
151
+ finished_at: new Date().toISOString(),
152
+ error: "User denied permissions",
153
+ };
154
+ }
155
+ }
156
+ const adapters = host.adapters ?? [];
157
+ for (const cap of pkg.manifest.capabilities) {
158
+ if (!cap.required)
159
+ continue;
160
+ const ok = adapters.some((a) => a.supports(cap));
161
+ if (!ok && cap.fallback === "fail") {
162
+ return {
163
+ kind: "skill_run",
164
+ id: runId,
165
+ skill_id: pkg.manifest.id,
166
+ skill_version: pkg.manifest.version,
167
+ package_digest: pkg.manifest.package_digest,
168
+ status: "failed",
169
+ mode,
170
+ resolved_inputs: resolved,
171
+ secret_refs,
172
+ steps: [],
173
+ verifications: [],
174
+ runtime: {
175
+ name: RUNTIME_NAME,
176
+ version: RUNTIME_VERSION,
177
+ host: host.host,
178
+ model: host.model,
179
+ },
180
+ started_at: started,
181
+ finished_at: new Date().toISOString(),
182
+ error: `Required capability unavailable: ${cap.name}`,
183
+ };
184
+ }
185
+ }
186
+ const byId = new Map(pkg.workflow.steps.map((s) => [s.id, s]));
187
+ let current = options.resume_from ?? pkg.workflow.entrypoint ?? pkg.manifest.entrypoint;
188
+ let toolCalls = 0;
189
+ const dry = mode === "dry_run";
190
+ while (current) {
191
+ const step = byId.get(current);
192
+ if (!step) {
193
+ return failRun(runId, pkg, mode, resolved, secret_refs, stepRecords, verifications, started, host, `Unknown step ${current}`);
194
+ }
195
+ const record = {
196
+ step_id: step.id,
197
+ kind: step.kind,
198
+ status: "pending",
199
+ started_at: new Date().toISOString(),
200
+ };
201
+ try {
202
+ if (step.kind === "checkpoint" && step.require_human && mode === "execute") {
203
+ record.status = "waiting";
204
+ record.finished_at = new Date().toISOString();
205
+ stepRecords.push(record);
206
+ return {
207
+ kind: "skill_run",
208
+ id: runId,
209
+ skill_id: pkg.manifest.id,
210
+ skill_version: pkg.manifest.version,
211
+ package_digest: pkg.manifest.package_digest,
212
+ status: "paused",
213
+ mode,
214
+ resolved_inputs: resolved,
215
+ secret_refs,
216
+ steps: stepRecords,
217
+ outputs,
218
+ verifications,
219
+ runtime: {
220
+ name: RUNTIME_NAME,
221
+ version: RUNTIME_VERSION,
222
+ host: host.host,
223
+ model: host.model,
224
+ },
225
+ checkpoints: [
226
+ {
227
+ id: `cp_${step.id}`,
228
+ step_id: step.id,
229
+ at: new Date().toISOString(),
230
+ state_digest: sha256Digest(JSON.stringify(stepOutputs)),
231
+ },
232
+ ],
233
+ started_at: started,
234
+ finished_at: new Date().toISOString(),
235
+ };
236
+ }
237
+ const result = await executeStep(step, {
238
+ inputs: resolved,
239
+ secret_refs,
240
+ stepOutputs,
241
+ pkg,
242
+ adapters,
243
+ dry,
244
+ host,
245
+ });
246
+ if (result.toolCall)
247
+ toolCalls += 1;
248
+ if (toolCalls > pkg.manifest.policy.max_tool_calls) {
249
+ throw new Error("max_tool_calls exceeded");
250
+ }
251
+ record.status = result.skipped ? "skipped" : "succeeded";
252
+ record.adapter = result.adapter;
253
+ record.output_digest = result.output !== undefined
254
+ ? sha256Digest(JSON.stringify(result.output))
255
+ : undefined;
256
+ if (result.output !== undefined) {
257
+ stepOutputs[step.id] = result.output;
258
+ if (result.resultAs)
259
+ stepOutputs[result.resultAs] = result.output;
260
+ }
261
+ if (result.emit) {
262
+ outputs[result.emit.name] = result.emit.value;
263
+ }
264
+ if (result.verifications)
265
+ verifications.push(...result.verifications);
266
+ record.finished_at = new Date().toISOString();
267
+ stepRecords.push(record);
268
+ current = result.next ?? nextStepId(step);
269
+ }
270
+ catch (e) {
271
+ record.status = "failed";
272
+ record.error = e instanceof Error ? e.message : String(e);
273
+ record.finished_at = new Date().toISOString();
274
+ stepRecords.push(record);
275
+ if (step.on_fail) {
276
+ current = step.on_fail;
277
+ continue;
278
+ }
279
+ if (step.optional) {
280
+ current = nextStepId(step);
281
+ continue;
282
+ }
283
+ return failRun(runId, pkg, mode, resolved, secret_refs, stepRecords, verifications, started, host, record.error);
284
+ }
285
+ }
286
+ for (const out of pkg.manifest.outputs) {
287
+ if (out.required && outputs[out.name] === undefined) {
288
+ const last = stepRecords.filter((s) => s.status === "succeeded").at(-1);
289
+ if (last && stepOutputs[last.step_id] !== undefined) {
290
+ outputs[out.name] = stepOutputs[last.step_id];
291
+ }
292
+ else {
293
+ verifications.push({
294
+ assertion: `output:${out.name}`,
295
+ passed: false,
296
+ detail: "Required output missing",
297
+ });
298
+ }
299
+ }
300
+ }
301
+ const failedVerify = verifications.some((v) => !v.passed);
302
+ return {
303
+ kind: "skill_run",
304
+ id: runId,
305
+ skill_id: pkg.manifest.id,
306
+ skill_version: pkg.manifest.version,
307
+ package_digest: pkg.manifest.package_digest,
308
+ status: failedVerify ? "failed" : "succeeded",
309
+ mode,
310
+ resolved_inputs: resolved,
311
+ secret_refs,
312
+ steps: stepRecords,
313
+ outputs,
314
+ verifications,
315
+ runtime: {
316
+ name: RUNTIME_NAME,
317
+ version: RUNTIME_VERSION,
318
+ host: host.host,
319
+ model: host.model,
320
+ },
321
+ started_at: started,
322
+ finished_at: new Date().toISOString(),
323
+ error: failedVerify ? "Verification failed" : undefined,
324
+ };
325
+ }
326
+ function nextStepId(step) {
327
+ if (!step.next)
328
+ return undefined;
329
+ return Array.isArray(step.next) ? step.next[0] : step.next;
330
+ }
331
+ function failRun(runId, pkg, mode, resolved, secret_refs, steps, verifications, started, host, error) {
332
+ return {
333
+ kind: "skill_run",
334
+ id: runId,
335
+ skill_id: pkg.manifest.id,
336
+ skill_version: pkg.manifest.version,
337
+ package_digest: pkg.manifest.package_digest,
338
+ status: "failed",
339
+ mode,
340
+ resolved_inputs: resolved,
341
+ secret_refs,
342
+ steps,
343
+ verifications,
344
+ runtime: {
345
+ name: RUNTIME_NAME,
346
+ version: RUNTIME_VERSION,
347
+ host: host.host,
348
+ model: host.model,
349
+ },
350
+ started_at: started,
351
+ finished_at: new Date().toISOString(),
352
+ error,
353
+ };
354
+ }
355
+ async function executeStep(step, ctx) {
356
+ switch (step.kind) {
357
+ case "instruct": {
358
+ const text = substitute(step.text, ctx.inputs);
359
+ const constraints = (ctx.pkg.workflow.constraints ?? [])
360
+ .map((c) => `- [${c.effect}] ${c.statement}`)
361
+ .join("\n");
362
+ return {
363
+ output: {
364
+ instruction: text,
365
+ knowledge_refs: step.knowledge_refs,
366
+ constraints,
367
+ dry_run: ctx.dry,
368
+ },
369
+ };
370
+ }
371
+ case "prompt": {
372
+ const rendered = substitute(step.template, {
373
+ ...ctx.inputs,
374
+ ...Object.fromEntries(Object.entries(step.input_bindings ?? {}).map(([k, v]) => [
375
+ k,
376
+ ctx.inputs[v] ?? ctx.stepOutputs[v],
377
+ ])),
378
+ });
379
+ return { output: { prompt: rendered } };
380
+ }
381
+ case "tool": {
382
+ const cap = ctx.pkg.manifest.capabilities.find((c) => c.name === step.capability);
383
+ if (!cap)
384
+ throw new Error(`Unknown capability ${step.capability}`);
385
+ if (ctx.dry) {
386
+ return {
387
+ output: { dry_run: true, capability: cap.name, arguments: step.arguments },
388
+ resultAs: step.result_as,
389
+ toolCall: true,
390
+ };
391
+ }
392
+ const adapter = ctx.adapters.find((a) => a.supports(cap));
393
+ if (!adapter) {
394
+ if (cap.fallback === "skip_if_optional" || step.optional) {
395
+ return { skipped: true };
396
+ }
397
+ if (cap.fallback === "ask_human") {
398
+ throw new Error(`Capability ${cap.name} requires human/tool adapter`);
399
+ }
400
+ throw new Error(`No adapter for capability ${cap.name}`);
401
+ }
402
+ const args = { ...(step.arguments ?? {}) };
403
+ for (const [k, bind] of Object.entries(step.argument_bindings ?? {})) {
404
+ args[k] = ctx.inputs[bind] ?? ctx.stepOutputs[bind];
405
+ }
406
+ const inv = await adapter.invoke(cap, args);
407
+ if (!inv.ok)
408
+ throw new Error(inv.error ?? "tool failed");
409
+ return {
410
+ output: inv.result,
411
+ resultAs: step.result_as,
412
+ toolCall: true,
413
+ adapter: inv.adapter,
414
+ };
415
+ }
416
+ case "transform": {
417
+ const input = step.input_from
418
+ ? ctx.stepOutputs[step.input_from] ?? ctx.inputs[step.input_from]
419
+ : ctx.stepOutputs;
420
+ if (step.expression === "identity") {
421
+ return { output: input, resultAs: step.result_as };
422
+ }
423
+ if (step.expression.startsWith("jsonpath:")) {
424
+ const key = step.expression.slice("jsonpath:".length).replace(/^\$\.?/, "");
425
+ const val = input && typeof input === "object"
426
+ ? input[key]
427
+ : undefined;
428
+ return { output: val, resultAs: step.result_as };
429
+ }
430
+ throw new Error(`Unsupported transform: ${step.expression}`);
431
+ }
432
+ case "branch": {
433
+ for (const c of step.cases) {
434
+ if (evalWhen(c.when, ctx.inputs, ctx.stepOutputs)) {
435
+ return { next: c.goto, output: { branched: c.goto } };
436
+ }
437
+ }
438
+ if (step.else)
439
+ return { next: step.else, output: { branched: step.else } };
440
+ return { output: { branched: null } };
441
+ }
442
+ case "iterate": {
443
+ const collection = ctx.inputs[step.over] ?? ctx.stepOutputs[step.over];
444
+ if (!Array.isArray(collection))
445
+ throw new Error(`iterate.over ${step.over} is not an array`);
446
+ const results = [];
447
+ for (const item of collection) {
448
+ results.push({ [step.as]: item, body: step.body });
449
+ }
450
+ return { output: results };
451
+ }
452
+ case "delegate": {
453
+ if (ctx.dry)
454
+ return { output: { dry_run: true, task: step.task }, resultAs: step.result_as };
455
+ throw new Error("delegate requires an A2A adapter (not configured)");
456
+ }
457
+ case "checkpoint": {
458
+ return { output: { checkpoint: true, message: step.message } };
459
+ }
460
+ case "human_decision": {
461
+ if (ctx.dry) {
462
+ return {
463
+ output: { awaiting_human: true, prompt: step.prompt, choices: step.choices },
464
+ resultAs: step.result_as,
465
+ };
466
+ }
467
+ const existing = (step.result_as ? ctx.inputs[step.result_as] : undefined) ?? ctx.inputs[step.id];
468
+ if (existing !== undefined) {
469
+ return { output: existing, resultAs: step.result_as };
470
+ }
471
+ throw new Error(`human_decision ${step.id} requires input`);
472
+ }
473
+ case "verify": {
474
+ const results = [];
475
+ for (const assertion of step.assertions) {
476
+ if (assertion === "all_required_inputs_resolved") {
477
+ const missing = ctx.pkg.manifest.inputs.filter((i) => i.required && ctx.inputs[i.name] === undefined);
478
+ results.push({
479
+ assertion,
480
+ passed: missing.length === 0,
481
+ detail: missing.length ? missing.map((m) => m.name).join(",") : undefined,
482
+ });
483
+ }
484
+ else if (assertion.startsWith("constraint_present:") ||
485
+ assertion.startsWith("honor:")) {
486
+ const cid = assertion.slice(assertion.indexOf(":") + 1);
487
+ const c = ctx.pkg.workflow.constraints?.find((x) => x.id === cid);
488
+ results.push({
489
+ assertion,
490
+ passed: Boolean(c),
491
+ detail: c
492
+ ? `Constraint is present (behavioral compliance is host responsibility): ${c.statement}`
493
+ : "Constraint missing",
494
+ });
495
+ }
496
+ else if (assertion.startsWith("exists:")) {
497
+ const key = assertion.slice("exists:".length);
498
+ const val = ctx.stepOutputs[key] ?? ctx.inputs[key];
499
+ results.push({ assertion, passed: val !== undefined });
500
+ }
501
+ else {
502
+ results.push({
503
+ assertion,
504
+ passed: !ctx.pkg.manifest.policy.fail_on_unsupported_step,
505
+ detail: ctx.pkg.manifest.policy.fail_on_unsupported_step
506
+ ? "Unsupported assertion"
507
+ : "Unrecognized assertion treated as advisory",
508
+ });
509
+ }
510
+ }
511
+ if (results.some((r) => !r.passed)) {
512
+ throw new Error(`Verification failed: ${results.filter((r) => !r.passed).map((r) => r.assertion).join(", ")}`);
513
+ }
514
+ return { output: { verified: true }, verifications: results };
515
+ }
516
+ case "emit": {
517
+ const value = ctx.stepOutputs[step.from] ?? ctx.inputs[step.from];
518
+ return { output: value, emit: { name: step.output, value } };
519
+ }
520
+ case "subskill": {
521
+ throw new Error(`subskill ${step.skill_id} not resolved in this runtime invocation`);
522
+ }
523
+ default: {
524
+ const _exhaustive = step;
525
+ void _exhaustive;
526
+ throw new Error("Unsupported step kind");
527
+ }
528
+ }
529
+ }
530
+ function evalWhen(expr, inputs, stepOutputs) {
531
+ const m = expr.match(/^input:([a-zA-Z0-9_]+)(?:==(.*))?$/);
532
+ if (!m)
533
+ return false;
534
+ const val = inputs[m[1]] ?? stepOutputs[m[1]];
535
+ if (m[2] === undefined)
536
+ return Boolean(val);
537
+ return String(val) === m[2];
538
+ }
539
+ export async function runSkillArchive(archive, host = {}, options = {}) {
540
+ const validation = validatePackageBytes(archive);
541
+ if (!validation.ok) {
542
+ const started = new Date().toISOString();
543
+ return {
544
+ kind: "skill_run",
545
+ id: `run_${randomUUID().replace(/-/g, "").slice(0, 16)}`,
546
+ skill_id: validation.manifest?.id ?? "invalid",
547
+ skill_version: validation.manifest?.version ?? "0.0.0",
548
+ package_digest: validation.manifest?.package_digest ?? "",
549
+ status: "failed",
550
+ mode: options.mode ?? "execute",
551
+ resolved_inputs: {},
552
+ steps: [],
553
+ verifications: validation.issues.map((i) => ({
554
+ assertion: i.code,
555
+ passed: false,
556
+ detail: i.message,
557
+ })),
558
+ runtime: { name: RUNTIME_NAME, version: RUNTIME_VERSION, host: host.host, model: host.model },
559
+ started_at: started,
560
+ finished_at: new Date().toISOString(),
561
+ error: "Package validation failed",
562
+ };
563
+ }
564
+ const unpacked = unpackSkill(archive);
565
+ const manifest = unpacked.manifest;
566
+ const trustProfile = options.trust_profile ??
567
+ (manifest.policy.require_anchor
568
+ ? "anchored"
569
+ : manifest.policy.require_minted || manifest.policy.require_signatures
570
+ ? "minted"
571
+ : manifest.policy.trust_profile ?? "open");
572
+ if (trustProfile !== "open") {
573
+ const trust = verifyMintTrust(archive, trustProfile, options.issuer_secret);
574
+ if (!trust.ok) {
575
+ const started = new Date().toISOString();
576
+ return {
577
+ kind: "skill_run",
578
+ id: `run_${randomUUID().replace(/-/g, "").slice(0, 16)}`,
579
+ skill_id: manifest.id,
580
+ skill_version: manifest.version,
581
+ package_digest: manifest.package_digest,
582
+ status: "failed",
583
+ mode: options.mode ?? "execute",
584
+ resolved_inputs: {},
585
+ steps: [],
586
+ verifications: trust.issues.map((issue) => ({
587
+ assertion: issue.code,
588
+ passed: issue.severity !== "error",
589
+ detail: issue.message,
590
+ })),
591
+ runtime: {
592
+ name: RUNTIME_NAME,
593
+ version: RUNTIME_VERSION,
594
+ host: host.host,
595
+ model: host.model,
596
+ },
597
+ started_at: started,
598
+ finished_at: new Date().toISOString(),
599
+ error: "Package trust verification failed",
600
+ };
601
+ }
602
+ }
603
+ return runSkillPackage(unpacked.raw, host, options);
604
+ }
605
+ export { inspectSkill, validatePackageBytes, unpackSkill, explainPackage as explain };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@dot-skill/runtime",
3
+ "version": "0.4.1",
4
+ "description": "Open .skill Protocol — reference workflow runtime",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Bharat Dudeja",
8
+ "url": "https://github.com/bharatdudeja13-cmd"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/dot-skill/dot-skill.git",
19
+ "directory": "packages/runtime"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "import": "./dist/index.js",
27
+ "types": "./dist/index.d.ts"
28
+ }
29
+ },
30
+ "scripts": {
31
+ "build": "rm -rf dist && tsc",
32
+ "prepack": "npm run build",
33
+ "clean": "rm -rf dist"
34
+ },
35
+ "dependencies": {
36
+ "@dot-skill/core": "^0.4.1",
37
+ "@dot-skill/protocol": "^0.4.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^26.1.1",
41
+ "typescript": "^5.4.5"
42
+ }
43
+ }