@skillerr/protocol 0.6.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.
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Multi-skill identification → scaffold extraction for agents.
3
+ *
4
+ * Segmentation is an agent/adapter responsibility. This module does not invent
5
+ * skills from free prose: the caller must supply candidate topics. It emits
6
+ * incomplete SkillContract / SkillSource scaffolds plus completeness reports
7
+ * so create flows stay coherent with SkillContract 0.5 and refuse incomplete
8
+ * release compiles.
9
+ */
10
+ import { createHash } from "node:crypto";
11
+ import { assessSkillContract, scaffoldSkillContract } from "./authoring.js";
12
+ import { PROTOCOL_VERSION } from "./types.js";
13
+ function slugify(title) {
14
+ const slug = title
15
+ .toLowerCase()
16
+ .replace(/[^a-z0-9]+/g, "-")
17
+ .replace(/^-+|-+$/g, "")
18
+ .slice(0, 64);
19
+ return slug || "skill-candidate";
20
+ }
21
+ function candidateId(title, index) {
22
+ const digest = createHash("sha256")
23
+ .update(`${index}:${title}`)
24
+ .digest("hex")
25
+ .slice(0, 12);
26
+ return `cand_${digest}`;
27
+ }
28
+ function normalizeCandidates(input) {
29
+ const fromCandidates = input.candidates ?? [];
30
+ const fromTopics = (input.topics ?? []).map((topic) => typeof topic === "string" ? { title: topic } : topic);
31
+ const merged = [...fromCandidates, ...fromTopics];
32
+ const seen = new Set();
33
+ const out = [];
34
+ for (const item of merged) {
35
+ const title = typeof item.title === "string" ? item.title.trim() : "";
36
+ if (!title)
37
+ continue;
38
+ const key = title.toLowerCase();
39
+ if (seen.has(key))
40
+ continue;
41
+ seen.add(key);
42
+ out.push({ ...item, title });
43
+ }
44
+ return out;
45
+ }
46
+ function parseJourney(raw) {
47
+ if (!raw || typeof raw !== "object") {
48
+ throw new Error('extract expects JSON object { summary, candidates|topics: [...] }. Run: skill agent-guide');
49
+ }
50
+ const obj = raw;
51
+ const summary = typeof obj.summary === "string"
52
+ ? obj.summary
53
+ : typeof obj.journey_summary === "string"
54
+ ? obj.journey_summary
55
+ : "";
56
+ if (!summary.trim()) {
57
+ throw new Error("extract requires a redacted journey summary string (summary or journey_summary).");
58
+ }
59
+ return {
60
+ kind: "redacted_journey",
61
+ summary: summary.trim(),
62
+ redacted: obj.redacted !== false,
63
+ sensitivity: obj.sensitivity === "private" ||
64
+ obj.sensitivity === "shareable_redacted" ||
65
+ obj.sensitivity === "public"
66
+ ? obj.sensitivity
67
+ : "shareable_redacted",
68
+ candidates: Array.isArray(obj.candidates)
69
+ ? obj.candidates
70
+ : undefined,
71
+ topics: Array.isArray(obj.topics)
72
+ ? obj.topics
73
+ : undefined,
74
+ };
75
+ }
76
+ function seedContract(title, intent, skillKind, sensitivity) {
77
+ const scaffold = scaffoldSkillContract();
78
+ scaffold.title = title;
79
+ scaffold.intent =
80
+ intent?.trim() ||
81
+ `__required__: state the transferable intent for “${title}” (what an agent must do).`;
82
+ if (skillKind)
83
+ scaffold.skill_kind = skillKind;
84
+ scaffold.sensitivity = sensitivity;
85
+ return scaffold;
86
+ }
87
+ function sourceScaffold(args) {
88
+ return {
89
+ kind: "skill_source",
90
+ id: args.id,
91
+ hash: `sha256:${"0".repeat(64)}`,
92
+ title: args.title,
93
+ summary: args.summary,
94
+ intent: args.contract.intent,
95
+ contract: args.contract,
96
+ candidates: undefined,
97
+ sections: [],
98
+ steering: [],
99
+ prompts: [],
100
+ code_refs: [],
101
+ parents: [],
102
+ agent: {
103
+ host: args.host,
104
+ deployment: "unknown",
105
+ },
106
+ journey: {
107
+ summary: args.summary,
108
+ redacted: true,
109
+ sensitivity: args.sensitivity,
110
+ },
111
+ inputs_declared: "inferred",
112
+ sensitivity: args.sensitivity,
113
+ created_at: new Date(0).toISOString(),
114
+ actor: { id: "agent" },
115
+ source_protocol_version: PROTOCOL_VERSION,
116
+ source_refs: args.evidenceRefs.map((ref, i) => ({
117
+ product: "extract",
118
+ kind: "evidence",
119
+ id: `ev_${i + 1}`,
120
+ hash: ref,
121
+ })),
122
+ };
123
+ }
124
+ const PROTOCOL_RULES = [
125
+ "Identify distinct transferable skills from the redacted journey; do not collapse unrelated topics into one skill.",
126
+ "One skill workspace per candidate (skill init in its own directory).",
127
+ "Fill a SkillContract 0.5 for each skill; section prose alone cannot satisfy release completeness.",
128
+ "Run skill status / skill contract-check; refuse release compile when incomplete.",
129
+ "Never invent filler declarations to force a mint; use continuity checkpoint for partial handoff.",
130
+ "Secrets stay as {{refs}} / env refs; journey text must stay redacted.",
131
+ ];
132
+ const CREATE_PATH = [
133
+ "skill agent-guide",
134
+ "Identify N candidate skills → write redacted_journey.json with summary + candidates|topics",
135
+ "skill extract redacted_journey.json -o ./extraction",
136
+ "For each selected candidate: mkdir workspace && cd workspace && skill init --title \"…\"",
137
+ "Copy/adapt contract scaffold → complete every declaration (or explicit none/not_applicable)",
138
+ "skill journey --summary \"…\" && skill propose … (evidence sections)",
139
+ "skill contract-check .skill/… or contract.json --profile release",
140
+ "skill status → skill compile -m \"…\" --approve --mint (or checkpoint if incomplete)",
141
+ ];
142
+ /**
143
+ * Emit incomplete SkillContract/SkillSource scaffolds for agent-identified candidates.
144
+ * Does not invent topics from free prose when candidates/topics are absent.
145
+ */
146
+ export function extractSkillCandidates(raw, options = {}) {
147
+ const journey = parseJourney(raw);
148
+ const profile = options.profile ?? "release";
149
+ const host = options.host ?? "__set_SKILL_HOST__";
150
+ const candidates = normalizeCandidates(journey);
151
+ if (candidates.length === 0) {
152
+ throw new Error("No skill candidates supplied. Identify distinct skills from the journey, then pass candidates:[{title,evidence_refs?}] or topics:[\"…\"]. See: skill agent-guide");
153
+ }
154
+ const scaffolds = candidates.map((item, index) => {
155
+ const id = item.id?.trim() || candidateId(item.title, index);
156
+ const evidence = item.evidence_refs?.filter((r) => typeof r === "string" && r.trim()) ??
157
+ [];
158
+ if (evidence.length === 0) {
159
+ evidence.push(`journey:${slugify(item.title)}`);
160
+ }
161
+ const contract = seedContract(item.title, item.intent, item.skill_kind, journey.sensitivity ?? "shareable_redacted");
162
+ const assessment = assessSkillContract(contract, profile);
163
+ const candidate = {
164
+ id,
165
+ title: item.title,
166
+ evidence_refs: evidence,
167
+ assessment,
168
+ // Incomplete scaffold is intentional; do not claim a complete contract.
169
+ contract: undefined,
170
+ };
171
+ const workspace_slug = slugify(item.title);
172
+ const missing = assessment.issues.map(({ field, message, fix }) => ({
173
+ field,
174
+ message,
175
+ fix,
176
+ }));
177
+ const source = sourceScaffold({
178
+ id: `src_${id.replace(/^cand_/, "")}`,
179
+ title: item.title,
180
+ summary: journey.summary,
181
+ contract,
182
+ evidenceRefs: evidence,
183
+ sensitivity: journey.sensitivity ?? "shareable_redacted",
184
+ host,
185
+ });
186
+ return {
187
+ candidate,
188
+ contract_scaffold: contract,
189
+ source_scaffold: source,
190
+ workspace_slug,
191
+ missing,
192
+ next_steps: [
193
+ `mkdir -p ${workspace_slug} && cd ${workspace_slug} && export SKILL_HOST=${host === "__set_SKILL_HOST__" ? "cursor" : host} && skill init --title ${JSON.stringify(item.title)}`,
194
+ "Complete contract_scaffold into a real SkillContract 0.5 (every declaration explicit).",
195
+ `skill contract-check contract.json --profile ${profile}`,
196
+ 'skill journey --summary "…" && skill propose --json \'[…]\'',
197
+ "skill status — refuse release compile until complete; use skill checkpoint for handoff.",
198
+ ],
199
+ };
200
+ });
201
+ return {
202
+ kind: "skill_extraction",
203
+ protocol_version: PROTOCOL_VERSION,
204
+ profile,
205
+ journey_summary: journey.summary,
206
+ redacted: journey.redacted !== false,
207
+ candidate_count: scaffolds.length,
208
+ scaffolds,
209
+ protocol: {
210
+ one_workspace_per_skill: true,
211
+ refuse_release_if_incomplete: true,
212
+ rules: PROTOCOL_RULES,
213
+ create_path: CREATE_PATH,
214
+ },
215
+ };
216
+ }
217
+ /** Alias for adapters that prefer "segment" vocabulary. */
218
+ export const segmentJourney = extractSkillCandidates;
219
+ /** Structured agent-facing create / multi-skill protocol (also printed by CLI). */
220
+ export function agentCreateGuide() {
221
+ return {
222
+ kind: "skill_agent_guide",
223
+ protocol_version: PROTOCOL_VERSION,
224
+ purpose: "Create portable .skill packages with SkillContract 0.5. Identify multiple skills from a conversation when warranted; enforce completeness; never fake a release.",
225
+ rules: PROTOCOL_RULES,
226
+ identify_multiple_skills: [
227
+ "Read the redacted journey / work summary. List distinct transferable skills (separate intents, triggers, or runtimes).",
228
+ "Do not invent a multi-skill auto-pipeline beyond this protocol. You identify; the CLI scaffolds.",
229
+ "Write JSON: { \"kind\":\"redacted_journey\", \"summary\":\"…\", \"redacted\":true, \"candidates\":[{ \"title\":\"…\", \"evidence_refs\":[\"…\"], \"intent\":\"…\", \"skill_kind\":\"procedure|knowledge|integration\" }] }",
230
+ "Run: skill extract journey.json -o ./extraction",
231
+ "Present candidates + missing[] reports to the human. Only proceed on selected skills.",
232
+ "One directory / one skill init per selected candidate. Never merge unrelated candidates into one workspace.",
233
+ ],
234
+ create_one_skill: [
235
+ "export SKILL_HOST=<your-host-id>",
236
+ "skill init --title \"…\"",
237
+ "skill journey --summary \"Redacted human+AI journey…\"",
238
+ "Complete SkillContract 0.5 (skill contract-template → edit → skill contract-check).",
239
+ "skill propose --json '[{\"title\":\"…\",\"body\":\"…\",\"type\":\"decision|integration|…\"}]' for evidence sections",
240
+ "skill status — inspect completeness / missing",
241
+ "Partial handoff: skill checkpoint -m \"WIP\"",
242
+ "Release only when complete: skill compile -m \"…\" --approve --mint",
243
+ "On compile_refused: list missing fields/fixes; do not pack a fake release.",
244
+ ],
245
+ ingest: [
246
+ "skill inspect ./file.skill",
247
+ "skill validate ./file.skill",
248
+ "skill verify-trust ./file.skill",
249
+ "skill load ./file.skill # continuity resume",
250
+ "skill run ./file.skill # dry-run by default",
251
+ ],
252
+ cli: [
253
+ "skill agent-guide [--json]",
254
+ "skill extract <journey.json> [-o dir] [--profile release|continuity]",
255
+ "skill segment … # alias of extract",
256
+ "skill contract-template",
257
+ "skill contract-check <contract-or-source.json> [--profile release|continuity]",
258
+ "skill status",
259
+ ],
260
+ refuse: [
261
+ "Refuse release compile/mint when contract assessment is incomplete.",
262
+ "Refuse to invent filler skills or filler contract fields to satisfy gates.",
263
+ "Refuse to create multiple skills in one workspace.",
264
+ "Refuse to proceed without SKILL_HOST on create/mint paths.",
265
+ ],
266
+ };
267
+ }
268
+ /** Human-readable text form of agentCreateGuide (for terminals / AGENT.md parity). */
269
+ export function formatAgentGuide(guide = agentCreateGuide()) {
270
+ const lines = [
271
+ `skill agent-guide — Open .skill Protocol v${guide.protocol_version}`,
272
+ "",
273
+ guide.purpose,
274
+ "",
275
+ "Rules:",
276
+ ...guide.rules.map((r) => ` - ${r}`),
277
+ "",
278
+ "Identify → propose multiple skills:",
279
+ ...guide.identify_multiple_skills.map((r, i) => ` ${i + 1}. ${r}`),
280
+ "",
281
+ "Create one skill (complete or refuse):",
282
+ ...guide.create_one_skill.map((r) => ` $ ${r}`),
283
+ "",
284
+ "Ingest / load / run:",
285
+ ...guide.ingest.map((r) => ` $ ${r}`),
286
+ "",
287
+ "CLI:",
288
+ ...guide.cli.map((r) => ` $ ${r}`),
289
+ "",
290
+ "Refuse when:",
291
+ ...guide.refuse.map((r) => ` - ${r}`),
292
+ "",
293
+ ];
294
+ return lines.join("\n");
295
+ }
296
+ /** Type guard helper for partial SkillContract assessments on scaffolds. */
297
+ export function assessExtractionScaffold(scaffold, profile = "release") {
298
+ return assessSkillContract(scaffold.contract_scaffold, profile);
299
+ }
@@ -0,0 +1,11 @@
1
+ /** Open .skill Protocol v0.5 — types, constants, authoring APIs, and schemas. */
2
+ export { PROTOCOL_VERSION, CONTAINER_VERSION, WORKFLOW_DIALECT_VERSION, MEDIA_TYPE, MANIFEST_MEDIA_TYPE, DEFAULT_SKILL_POLICY, } from "./types.js";
3
+ export type { SkillCompileProfile, PackageSensitivity, ProvenanceMode, MintStatus, PermanenceAnchorKind, TrustProfile, TrustState, HostClaimBinding, IssuerClass, InputSource, SensitivityLevel, AskWhen, SideEffectClass, CapabilityFallback, KnowledgeItemType, SteeringEffect, WorkflowStepKind, SkillRunStatus, RuntimeMode, CompletenessPart, GenerationUsage, JourneyProvenance, CompletenessReport, JsonSchema, ContentDigest, ProvenanceRef, InputSlot, OutputContract, CapabilityAdapterHint, CapabilityRequirement, SkillPermission, SkillPolicy, SkillDependency, MintRecord, SealedManifestClaims, CreationAttestation, TrustView, PermanenceAnchor, SkillManifest, KnowledgeItem, SteeringConstraint, WorkflowStepBase, InstructStep, PromptStep, ToolStep, TransformStep, BranchStep, IterateStep, DelegateStep, CheckpointStep, HumanDecisionStep, VerifyStep, EmitStep, SubskillStep, WorkflowStep, Workflow, CompilationIssue, CompilationMapping, CompilationReport, SkillPackageFiles, SkillStepRecord, SkillRun, } from "./types.js";
4
+ export { FORBIDDEN_AGENT_HOSTS, AGENT_RUNTIME_MARKER_ENVS, isValidAgentHost, detectAgentRuntimeMarkers, hasAgentRuntimeEvidence, } from "./source.js";
5
+ export { assessSkillContract, scaffoldSkillContract, explainContractAssessment, } from "./authoring.js";
6
+ export { extractSkillCandidates, segmentJourney, agentCreateGuide, formatAgentGuide, assessExtractionScaffold, } from "./extract.js";
7
+ export type { JourneyCandidateInput, RedactedJourneyInput, ExtractionScaffold, ExtractionReport, AgentGuide, } from "./extract.js";
8
+ export type { SkillKind, DeclarationStatus, ExplicitDeclaration, ContractTrigger, InputApproval, ContractInput, ContractPrecondition, ContractStepKind, ContractStep, ContractBranch, ContractHumanDecision, ContractCapability, ContractPermission, ForbiddenAction, ContractOutput, RecoveryEdge, VerificationAssertion, ContractCorrection, ContractEvidence, ContractProvenance, SkillContract, ContractField, ContractIssue, ContractAssessment, SkillCandidate, } from "./contract.js";
9
+ export type { SectionType, SectionAuthor, CodeRef, Attachment, PersonRef, SkillSection, SteeringEvent, PromptVersion, AgentContext, SkillSource, SteeringVerb, CaptureFidelity, } from "./source.js";
10
+ export { recipeToSkillSource } from "./recipe.js";
11
+ export type { IngredientType, VisibilityIntent, RecipeIngredient, Recipe, Skill, } from "./recipe.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /** Open .skill Protocol v0.5 — types, constants, authoring APIs, and schemas. */
2
+ export { PROTOCOL_VERSION, CONTAINER_VERSION, WORKFLOW_DIALECT_VERSION, MEDIA_TYPE, MANIFEST_MEDIA_TYPE, DEFAULT_SKILL_POLICY, } from "./types.js";
3
+ export { FORBIDDEN_AGENT_HOSTS, AGENT_RUNTIME_MARKER_ENVS, isValidAgentHost, detectAgentRuntimeMarkers, hasAgentRuntimeEvidence, } from "./source.js";
4
+ export { assessSkillContract, scaffoldSkillContract, explainContractAssessment, } from "./authoring.js";
5
+ export { extractSkillCandidates, segmentJourney, agentCreateGuide, formatAgentGuide, assessExtractionScaffold, } from "./extract.js";
6
+ export { recipeToSkillSource } from "./recipe.js";
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Skillerr-shaped Recipe types — product adapter, not protocol vocabulary.
3
+ *
4
+ * Open protocol uses SkillSource / SkillSection (see source.ts).
5
+ * Skillerr (and similar products) map recipe/ingredient/bake → SkillSource/compile.
6
+ */
7
+ import type { GenerationUsage, JourneyProvenance } from "./types.js";
8
+ import type { SkillContract } from "./contract.js";
9
+ import type { AgentContext, Attachment, CodeRef, PersonRef, PromptVersion, SectionType, SkillSource, SteeringEvent } from "./source.js";
10
+ /** @deprecated Prefer SectionType from source.ts — kept for Skillerr adapters. */
11
+ export type IngredientType = SectionType;
12
+ export type VisibilityIntent = "private" | "publishable";
13
+ export type { SteeringVerb, CaptureFidelity } from "./source.js";
14
+ export type { CodeRef, Attachment, PersonRef, SteeringEvent, PromptVersion } from "./source.js";
15
+ /** @deprecated Prefer SkillSection — Skillerr ingredient shape. */
16
+ export interface RecipeIngredient {
17
+ id: string;
18
+ revision: number;
19
+ type: IngredientType;
20
+ title: string;
21
+ body: string;
22
+ attachments: Attachment[];
23
+ code_refs: CodeRef[];
24
+ sensitivity: VisibilityIntent;
25
+ }
26
+ /**
27
+ * @deprecated Prefer SkillSource.
28
+ * Recipe is Skillerr's capture document; adapters convert it before compile.
29
+ */
30
+ export interface Recipe {
31
+ kind: "recipe";
32
+ id: string;
33
+ hash: string;
34
+ title: string;
35
+ summary?: string;
36
+ ingredients: RecipeIngredient[];
37
+ steering: SteeringEvent[];
38
+ prompts: PromptVersion[];
39
+ code_refs: CodeRef[];
40
+ parents: string[];
41
+ provenance: {
42
+ hosts: string[];
43
+ models: string[];
44
+ session_ids: string[];
45
+ };
46
+ visibility_intent: VisibilityIntent;
47
+ baked_at: string;
48
+ baker: PersonRef;
49
+ source_protocol_version: string;
50
+ generation_usage?: GenerationUsage;
51
+ journey_summary?: string;
52
+ /** Optional 0.5 protocol-native semantics carried through this lossy product adapter. */
53
+ contract?: SkillContract;
54
+ }
55
+ /** @deprecated Legacy flat markdown skill export. Prefer SkillManifest / `.skill`. */
56
+ export interface Skill {
57
+ kind: "skill";
58
+ id: string;
59
+ version: string;
60
+ title: string;
61
+ body: string;
62
+ sources: Array<{
63
+ kind: "ingredient" | "recipe" | "section";
64
+ id: string;
65
+ revision?: number;
66
+ hash?: string;
67
+ }>;
68
+ exported_at: string;
69
+ source_protocol_version: string;
70
+ }
71
+ /** Map a Skillerr Recipe into protocol SkillSource. */
72
+ export declare function recipeToSkillSource(recipe: Recipe, overrides?: {
73
+ agent?: Partial<AgentContext>;
74
+ journey?: Partial<JourneyProvenance>;
75
+ }): SkillSource;
package/dist/recipe.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Skillerr-shaped Recipe types — product adapter, not protocol vocabulary.
3
+ *
4
+ * Open protocol uses SkillSource / SkillSection (see source.ts).
5
+ * Skillerr (and similar products) map recipe/ingredient/bake → SkillSource/compile.
6
+ */
7
+ import { isValidAgentHost } from "./source.js";
8
+ function visibilityToSensitivity(v) {
9
+ return v === "publishable" ? "public" : "private";
10
+ }
11
+ /** Map a Skillerr Recipe into protocol SkillSource. */
12
+ export function recipeToSkillSource(recipe, overrides = {}) {
13
+ const host = overrides.agent?.host ?? recipe.provenance.hosts[0];
14
+ if (!isValidAgentHost(host)) {
15
+ throw new Error("AI agent host required to adapt Recipe → SkillSource. Set a real host (e.g. cursor, claude), not human/cli.");
16
+ }
17
+ const sections = recipe.ingredients.map((ing) => ({
18
+ id: ing.id,
19
+ revision: ing.revision,
20
+ type: ing.type,
21
+ title: ing.title,
22
+ body: ing.body,
23
+ attachments: ing.attachments,
24
+ code_refs: ing.code_refs,
25
+ sensitivity: visibilityToSensitivity(ing.sensitivity),
26
+ authored_by: "agent",
27
+ }));
28
+ const journey = {
29
+ summary: overrides.journey?.summary ??
30
+ recipe.journey_summary ??
31
+ recipe.summary ??
32
+ `Human+AI work captured as recipe ${recipe.id}: ${recipe.title}`,
33
+ open_questions: overrides.journey?.open_questions,
34
+ decisions: overrides.journey?.decisions ??
35
+ recipe.ingredients.filter((i) => i.type === "decision").map((i) => i.title),
36
+ redacted: overrides.journey?.redacted ?? true,
37
+ sensitivity: overrides.journey?.sensitivity ??
38
+ (recipe.visibility_intent === "publishable" ? "public" : "shareable_redacted"),
39
+ };
40
+ const declaresInputs = recipe.ingredients.some((ing) => /\{\{[a-zA-Z_][a-zA-Z0-9_]*\}\}|<([A-Z][A-Z0-9_]+)>|\$\{[a-zA-Z_][a-zA-Z0-9_]*\}/.test(ing.body));
41
+ return {
42
+ kind: "skill_source",
43
+ id: recipe.id,
44
+ hash: recipe.hash,
45
+ title: recipe.title,
46
+ summary: recipe.summary,
47
+ intent: recipe.summary ?? recipe.title,
48
+ contract: recipe.contract ? structuredClone(recipe.contract) : undefined,
49
+ sections,
50
+ steering: recipe.steering,
51
+ prompts: recipe.prompts,
52
+ code_refs: recipe.code_refs,
53
+ parents: recipe.parents,
54
+ agent: {
55
+ host: host,
56
+ provider: overrides.agent?.provider,
57
+ model: overrides.agent?.model ?? recipe.provenance.models[0],
58
+ runtime: overrides.agent?.runtime,
59
+ deployment: overrides.agent?.deployment,
60
+ endpoint: overrides.agent?.endpoint,
61
+ session_ids: overrides.agent?.session_ids ?? recipe.provenance.session_ids,
62
+ },
63
+ journey,
64
+ generation_usage: recipe.generation_usage,
65
+ inputs_declared: declaresInputs ? "inferred" : "none",
66
+ sensitivity: visibilityToSensitivity(recipe.visibility_intent) === "public"
67
+ ? "public"
68
+ : "shareable_redacted",
69
+ created_at: recipe.baked_at,
70
+ actor: recipe.baker,
71
+ source_protocol_version: recipe.source_protocol_version,
72
+ source_refs: [{ product: "skillerr", kind: "recipe", id: recipe.id, hash: recipe.hash }],
73
+ };
74
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Protocol-native source for the skill compiler.
3
+ *
4
+ * Products (Skillerr, etc.) may use their own words (ingredient, recipe, bake).
5
+ * Adapters map those into SkillSource / SkillSection before compile.
6
+ */
7
+ import type { SkillContract, SkillCandidate } from "./contract.js";
8
+ import type { GenerationUsage, JourneyProvenance, PackageSensitivity } from "./types.js";
9
+ export type SectionType = "prompt" | "decision" | "architecture" | "diagram" | "integration" | "resource" | "reference" | "lesson" | "requirement" | "tradeoff" | "risk" | "question" | "implementation_note" | "config" | "correction_note" | "doc" | "message" | "handoff" | "code" | "intent" | "workflow_note";
10
+ export type SectionAuthor = "agent" | "human_via_agent";
11
+ export interface CodeRef {
12
+ forge?: string;
13
+ repo: string;
14
+ commit?: string;
15
+ path?: string;
16
+ range?: string;
17
+ pr?: number;
18
+ }
19
+ export interface Attachment {
20
+ id: string;
21
+ kind: "diagram" | "image" | "config" | "file" | "other";
22
+ title?: string;
23
+ content?: string;
24
+ uri?: string;
25
+ mediaType?: string;
26
+ }
27
+ export interface PersonRef {
28
+ id: string;
29
+ display_name?: string;
30
+ }
31
+ export type SteeringVerb = "affirm" | "correct" | "reject";
32
+ export type CaptureFidelity = "exact" | "synthesize";
33
+ export interface SkillSection {
34
+ id: string;
35
+ revision: number;
36
+ type: SectionType;
37
+ title: string;
38
+ body: string;
39
+ attachments: Attachment[];
40
+ code_refs: CodeRef[];
41
+ /** Never embed secret values — sensitivity guides redaction. */
42
+ sensitivity: PackageSensitivity;
43
+ /** Declared authoring path for this section. */
44
+ authored_by: SectionAuthor;
45
+ }
46
+ export interface SteeringEvent {
47
+ kind: "steering";
48
+ id: string;
49
+ session_id: string;
50
+ verb: SteeringVerb;
51
+ target_kind: "section" | "turn" | "other" | "ingredient";
52
+ target_id: string;
53
+ note?: string;
54
+ actor: PersonRef;
55
+ at: string;
56
+ }
57
+ export interface PromptVersion {
58
+ kind: "prompt";
59
+ id: string;
60
+ lineage_id: string;
61
+ version: number;
62
+ body: string;
63
+ origin: "user" | "ai_generated" | "imported";
64
+ parent_version?: number;
65
+ session_id?: string;
66
+ created_at: string;
67
+ }
68
+ /** Required AI agent identity for any compile path. */
69
+ export interface AgentContext {
70
+ /** Host/app that ran the agent: cursor | ollama | lmstudio | custom-agent | … */
71
+ host: string;
72
+ /** Model provider/runtime family; provider-neutral and local-friendly. */
73
+ provider?: string;
74
+ model?: string;
75
+ runtime?: string;
76
+ /** Where inference ran. This is provenance, not proof. */
77
+ deployment?: "local" | "hosted" | "hybrid" | "unknown";
78
+ /** Optional endpoint identifier. Must not contain credentials. */
79
+ endpoint?: string;
80
+ session_ids?: string[];
81
+ }
82
+ /**
83
+ * Protocol input to the compiler.
84
+ * Products adapt their capture model into this shape.
85
+ */
86
+ export interface SkillSource {
87
+ kind: "skill_source";
88
+ id: string;
89
+ hash: string;
90
+ title: string;
91
+ summary?: string;
92
+ intent?: string;
93
+ /**
94
+ * 0.5 source of truth for transferable semantics. A missing contract marks
95
+ * a 0.4-compatible text source and is release-lossy.
96
+ */
97
+ contract?: SkillContract;
98
+ /** Optional extraction candidates. Segmentation belongs to an adapter/AI, not the compiler. */
99
+ candidates?: SkillCandidate[];
100
+ sections: SkillSection[];
101
+ steering: SteeringEvent[];
102
+ prompts: PromptVersion[];
103
+ code_refs: CodeRef[];
104
+ parents: string[];
105
+ agent: AgentContext;
106
+ journey: JourneyProvenance;
107
+ generation_usage?: GenerationUsage;
108
+ /** Explicitly declare that the source needs no runtime inputs. */
109
+ inputs_declared?: "inferred" | "none";
110
+ sensitivity: PackageSensitivity;
111
+ created_at: string;
112
+ actor: PersonRef;
113
+ source_protocol_version: string;
114
+ /** Optional product-specific source refs (e.g. Skillerr recipe id). */
115
+ source_refs?: Array<{
116
+ product: string;
117
+ kind: string;
118
+ id: string;
119
+ hash?: string;
120
+ }>;
121
+ }
122
+ /**
123
+ * Hosts that are not valid AI agent runtimes for skill creation / mint.
124
+ * Humans exporting SKILL_HOST=cli|shell|manual must never mint as an agent.
125
+ */
126
+ export declare const FORBIDDEN_AGENT_HOSTS: Set<string>;
127
+ export declare function isValidAgentHost(host: string | undefined | null): boolean;
128
+ /**
129
+ * Process / mint markers that indicate an agent runtime path (not a bare human shell).
130
+ * These are still spoofable by a determined local process — residual risk remains —
131
+ * but a human who only exports SKILL_HOST=cursor without any agent context fails this check.
132
+ */
133
+ export declare const AGENT_RUNTIME_MARKER_ENVS: readonly ["CURSOR_AGENT", "CURSOR_TRACE_ID", "COMPOSER_SESSION_ID", "SKILL_AGENT_INVOCATION", "SKILL_SESSION_ID", "CLAUDE_CODE_ENTRYPOINT", "AIDER_ACTIVE"];
134
+ export declare function detectAgentRuntimeMarkers(env?: Record<string, string | undefined>): string[];
135
+ export declare function hasAgentRuntimeEvidence(evidence?: {
136
+ markers?: string[];
137
+ session_id?: string;
138
+ } | null, env?: Record<string, string | undefined>): boolean;
package/dist/source.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Protocol-native source for the skill compiler.
3
+ *
4
+ * Products (Skillerr, etc.) may use their own words (ingredient, recipe, bake).
5
+ * Adapters map those into SkillSource / SkillSection before compile.
6
+ */
7
+ /**
8
+ * Hosts that are not valid AI agent runtimes for skill creation / mint.
9
+ * Humans exporting SKILL_HOST=cli|shell|manual must never mint as an agent.
10
+ */
11
+ export const FORBIDDEN_AGENT_HOSTS = new Set([
12
+ "",
13
+ "human",
14
+ "manual",
15
+ "none",
16
+ "cli",
17
+ "user",
18
+ "shell",
19
+ "bash",
20
+ "zsh",
21
+ "sh",
22
+ "fish",
23
+ "powershell",
24
+ "pwsh",
25
+ "cmd",
26
+ "terminal",
27
+ "console",
28
+ "tty",
29
+ "stdin",
30
+ "keyboard",
31
+ "local-shell",
32
+ "human-cli",
33
+ "operator",
34
+ ]);
35
+ export function isValidAgentHost(host) {
36
+ if (!host)
37
+ return false;
38
+ return !FORBIDDEN_AGENT_HOSTS.has(host.trim().toLowerCase());
39
+ }
40
+ /**
41
+ * Process / mint markers that indicate an agent runtime path (not a bare human shell).
42
+ * These are still spoofable by a determined local process — residual risk remains —
43
+ * but a human who only exports SKILL_HOST=cursor without any agent context fails this check.
44
+ */
45
+ export const AGENT_RUNTIME_MARKER_ENVS = [
46
+ "CURSOR_AGENT",
47
+ "CURSOR_TRACE_ID",
48
+ "COMPOSER_SESSION_ID",
49
+ "SKILL_AGENT_INVOCATION",
50
+ "SKILL_SESSION_ID",
51
+ "CLAUDE_CODE_ENTRYPOINT",
52
+ "AIDER_ACTIVE",
53
+ ];
54
+ export function detectAgentRuntimeMarkers(env = process.env) {
55
+ const found = [];
56
+ for (const key of AGENT_RUNTIME_MARKER_ENVS) {
57
+ const v = env[key];
58
+ if (v !== undefined && String(v).trim() !== "")
59
+ found.push(key);
60
+ }
61
+ return found;
62
+ }
63
+ export function hasAgentRuntimeEvidence(evidence, env = process.env) {
64
+ if (evidence?.session_id && evidence.session_id.trim())
65
+ return true;
66
+ if (evidence?.markers?.some((m) => m.trim()))
67
+ return true;
68
+ return detectAgentRuntimeMarkers(env).length > 0;
69
+ }