{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../../src/core/long-horizon/adaptive/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,aAAa,EAAE,kBAAkB,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACnB;AAyBD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,eAAe,CAsDxE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,aAAa,EACpB,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,EACvC,cAAc,EAAE,OAAO,EACvB,eAAe,EAAE,OAAO,GACtB,oBAAoB,CA8BtB;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAE9D","sourcesContent":["/**\n * Typed skill system.\n *\n * Skills are data/configuration — never automatically trusted code. A skill\n * manifest declares typed inputs, an output schema, allowed tools, denied\n * effects, execution mode, budget, timeout, success criteria, a model role,\n * version, and provenance. A skill can never grant capabilities the parent run\n * lacks: effective permissions are the intersection of user authorization ∩\n * parent policy ∩ skill allowlist ∩ execution mode. Repository Markdown is\n * never executed as a skill unless it has a recognized manifest + schema.\n */\n\nimport type { ModelRole, SkillExecutionMode, SkillManifest } from \"./types.js\";\n\nexport interface SkillValidation {\n\tvalid: boolean;\n\terrors: string[];\n\twarnings: string[];\n}\n\nexport interface EffectiveSkillPolicy {\n\tallowedTools: ReadonlySet<string>;\n\tdeniedEffects: ReadonlySet<string>;\n\texecutionMode: SkillExecutionMode;\n\tcanPublish: boolean;\n\tcanMutate: boolean;\n}\n\n/** Tools that a non-mutate skill may never claim, regardless of allowlist. */\nconst MUTATION_ONLY_TOOLS: ReadonlySet<string> = new Set([\n\t\"write_file\",\n\t\"edit\",\n\t\"delete\",\n\t\"git_commit\",\n\t\"git_push\",\n\t\"git_merge\",\n\t\"publish\",\n\t\"npm_publish\",\n\t\"mutation\",\n]);\n\nconst OBSERVE_COMPATIBLE_TOOLS: ReadonlySet<string> = new Set([\n\t\"read_file\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"git_status\",\n\t\"git_log\",\n\t\"git_diff\",\n]);\n\nexport function validateSkillManifest(manifest: unknown): SkillValidation {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tif (typeof manifest !== \"object\" || manifest === null) {\n\t\treturn { valid: false, errors: [\"NOT_AN_OBJECT\"], warnings };\n\t}\n\tconst m = manifest as Record<string, unknown>;\n\n\tif (typeof m.name !== \"string\" || m.name.length === 0) errors.push(\"NAME_REQUIRED\");\n\tif (typeof m.version !== \"number\" || m.version < 1) errors.push(\"VERSION_REQUIRED\");\n\tif (typeof m.description !== \"string\" || (m.description ?? \"\").length === 0) errors.push(\"DESCRIPTION_REQUIRED\");\n\tif (!Array.isArray(m.allowedTools) || !m.allowedTools.every((t) => typeof t === \"string\")) {\n\t\terrors.push(\"ALLOWED_TOOLS_REQUIRED\");\n\t}\n\tif (!Array.isArray(m.deniedEffects) || !m.deniedEffects.every((t) => typeof t === \"string\")) {\n\t\terrors.push(\"DENIED_EFFECTS_REQUIRED\");\n\t}\n\tif (!Array.isArray(m.successCriteria) || m.successCriteria.length === 0) errors.push(\"SUCCESS_CRITERIA_REQUIRED\");\n\n\tconst mode = m.executionMode;\n\tif (mode !== \"observe\" && mode !== \"static\" && mode !== \"mutate\") {\n\t\terrors.push(\"INVALID_EXECUTION_MODE\");\n\t}\n\n\tif (\n\t\tArray.isArray(m.allowedTools) &&\n\t\tm.allowedTools.some((t) => MUTATION_ONLY_TOOLS.has(t as string)) &&\n\t\tmode !== \"mutate\"\n\t) {\n\t\terrors.push(\"MUTATION_TOOL_WITHOUT_MUTATE_MODE\");\n\t}\n\n\tif (m.inputs !== undefined) {\n\t\tif (!Array.isArray(m.inputs)) {\n\t\t\terrors.push(\"INPUTS_MUST_BE_ARRAY\");\n\t\t} else {\n\t\t\tfor (const input of m.inputs) {\n\t\t\t\tif (\n\t\t\t\t\ttypeof input !== \"object\" ||\n\t\t\t\t\tinput === null ||\n\t\t\t\t\ttypeof (input as { name?: unknown }).name !== \"string\" ||\n\t\t\t\t\ttypeof (input as { type?: unknown }).type !== \"string\"\n\t\t\t\t) {\n\t\t\t\t\terrors.push(\"INPUT_MUST_HAVE_NAME_AND_TYPE\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (m.deniedEffects !== undefined && !Array.isArray(m.deniedEffects)) {\n\t\terrors.push(\"DENIED_EFFECTS_MUST_BE_ARRAY\");\n\t}\n\n\treturn { valid: errors.length === 0, errors, warnings };\n}\n\n/**\n * Compute the effective policy as the intersection of parent allowlist, skill\n * allowlist, and execution mode. A skill cannot authorize tools the parent run\n * does not allow, and observe/static modes never mutate or publish.\n */\nexport function computeEffectiveSkillPolicy(\n\tskill: SkillManifest,\n\tparentAllowedTools: ReadonlySet<string>,\n\tuserCanPublish: boolean,\n\tparentCanMutate: boolean,\n): EffectiveSkillPolicy {\n\tconst mutationOnlyInManifest = skill.allowedTools.some((t) => MUTATION_ONLY_TOOLS.has(t) || t === \"mutation\");\n\tconst modePermitsMutation = skill.executionMode === \"mutate\";\n\n\tconst allowedTools = new Set<string>();\n\tfor (const tool of skill.allowedTools) {\n\t\tif (!parentAllowedTools.has(tool) && tool !== \"mutation\") continue;\n\t\tif (MUTATION_ONLY_TOOLS.has(tool) && !(modePermitsMutation && parentCanMutate)) continue;\n\t\tif (OBSERVE_COMPATIBLE_TOOLS.has(tool) || modePermitsMutation || skill.executionMode === \"static\") {\n\t\t\tallowedTools.add(tool);\n\t\t}\n\t}\n\n\tconst deniedEffects = new Set(skill.deniedEffects);\n\tconst canPublish = false; // a skill can never authorize publication\n\tconst canMutate =\n\t\tuserCanPublish &&\n\t\tmodePermitsMutation &&\n\t\tparentCanMutate &&\n\t\t!deniedEffects.has(\"writesWorkspace\") &&\n\t\t!deniedEffects.has(\"mutatesGit\") &&\n\t\t!mutationOnlyInManifest;\n\n\treturn {\n\t\tallowedTools,\n\t\tdeniedEffects,\n\t\texecutionMode: skill.executionMode,\n\t\tcanPublish,\n\t\tcanMutate,\n\t};\n}\n\n/** Inspect the default model role for a skill (bounded). */\nexport function skillModelRole(skill: SkillManifest): ModelRole {\n\treturn skill.modelRole ?? \"subagent\";\n}\n"]}